Когда указатель в C определяет объем памяти, на который он ссылается?

91
8

Я узнал о связанных списках, и рекурсивное определение структуры node прослушивало меня


struct node {
struct node *next;
int data;
};

Я предполагаю, что я всегда думал, что, поскольку указатель вводится, он знает как начальный адрес, так и объем памяти, к которому он может получить доступ, когда он был разыменован во время объявления. Но это невозможно, так как оно объявлено перед произвольным количеством других переменных, которые могут сделать структуру любого размера. Означает ли это это только при разыменовании или есть какая-то таблица памяти, которая заполняется в конце определения структуры и до того, как можно использовать указатель?

спросил(а) 2021-01-19T20:07:35+03:00 9 месяцев назад
1
Решение
136

Указатель - это просто одно значение, он содержит один адрес в памяти.


Именно компилятор знает размер структур и смещений полей в этих структурах. Всякий раз, когда вы обращаетесь к полю в ссылочной структуре, он добавляет смещение.


Посмотрите на следующую программу:


struct X {
char a;
int b;
long c;
};

void y() {
struct X x;
x.a = 42;
x.b = 43;
x.c = 44;
}

Функция y преобразуется в следующий код ассемблера (gcc -s):


y:  
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movb $42, -16(%rbp)
movl $43, -12(%rbp)
movq $44, -8(%rbp)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc

Вы можете четко видеть значения 42, 43, 44. Компилятор вычисляет смещения полей в структуре x. Они относятся к указателю стека (rbp), поскольку значение x выделяется в стеке.

ответил(а) 2021-01-19T20:07:35+03:00 9 месяцев назад
65

Во время объявления структуры (с элементом как указателем на тот же тип) требуется только знать о типе, т.е. указателе на тип. Возможно, в этот момент тип не является полным, но указатель на тип известен для этой платформы.


Другими словами, это та же причина, по которой вы не можете иметь член этого типа структуры внутри объявления структуры (в этот момент структура не завершена), но может иметь указатель на тип.


Как только вы укажете член-указатель на какую-то действительную память, он также знает начальный адрес. Затем, после разыменования, он вычисляет адрес смещения и получает значение из этого местоположения на основе ранее упомянутого "типа".

ответил(а) 2021-01-19T20:07:35+03:00 9 месяцев назад
65

Edit


Derp. Теперь я понимаю вопрос, который вы действительно спрашиваете.


Есть две части. Во-первых, компилятор позволяет создавать указатели на "неполные" типы, где размер еще не известен. Во-вторых, все указатели на типы struct имеют одинаковый размер и представление, независимо от размера фактического типа struct.


Переход по вашему примеру:


struct node {
struct node *next;
int data;
};

Когда компилятор видит объявление для next, тип struct node является неполным - компилятор пока не знает, насколько велика struct node. Однако в этот момент процесса не нужно знать этот размер, чтобы вы могли объявить указатель на этот тип. Вы еще не в точке, где компилятор должен знать sizeof *next.


Определение типа завершено, когда компилятор видит закрытие }; определения struct - в этот момент компилятор знает, насколько велик тип struct node.

Оригинал


Компилятор знает размер заостренного типа, поэтому, указав указатель p, выражение p + 1 даст адрес следующего объекта указанного типа.


Учитывая


int    *ip = 0x1000; // 4 bytes
char *cp = 0x1000; // 1 byte
double *dp = 0x1000; // 8 bytes

выражение ip + 1 приведет к адресу следующего 4-байтового объекта int, или 0x1004, cp + 1 приведет к адресу следующего 1-байтового объекта char или 0x1001, а dp + 1 - адрес следующего 8-байтового объекта double или 0x1008.


Указатель сам указывает на один объект, период. Он не может узнать, является ли объект, на который он указывает, частью последовательности, или насколько велика любая такая последовательность.

ответил(а) 2021-01-19T20:07:35+03:00 9 месяцев назад
46

Каждый указатель имеет одинаковый размер в зависимости от вашей системы (4 или 8 байт, насколько я видел).


Итак, когда вы вводите такую ​​структуру


struct node {
struct node *next; // 4 or 8 bytes
int data; // 4 or 8 bytes
};

компилятор знает, что он точный размер.


Но попробуйте этот путь вместо этого и посмотрите сами.


//Wrong declaration
struct node {
struct node next; // The compiler cannot decide structure size
int data; // 4 or 8 bytes
};

Это даст ошибку компиляции.

ответил(а) 2021-01-19T20:07:35+03:00 9 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема