Когда указатель в C вычисляет объем памяти, на который он ссылается?
Я изучал связанные списки, и рекурсивное определение структуры узла не давало мне покоя
struct node {
struct node *next;
int data;
};
Я думаю, что всегда предполагал, что поскольку указатель типизирован, он знает и начальный адрес, и объем памяти, к которой он может получить доступ, когда он разыменован, во время объявления. Но это невозможно, так как он объявлен перед произвольным количеством других переменных, которые могут сделать структуру любого размера. Выясняет ли он это только при разыменовании, или существует ли какая-то таблица памяти, которая заполняется в конце определения структуры и до того, как указатель может быть использован?
4 ответа:
Edit
Сумасшедший. Теперь я понимаю вопрос, который вы на самом деле задаете.
Есть две части к этому. Во-первых, компилятор позволяет создавать указатели на "неполные" типы, размер которых еще не известен. Во-вторых, все указатели на типы
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
.Сам указатель указывает на один объект, точку. Он не может знать, является ли объект, на который он указывает, частью последовательности, или насколько велика любая такая последовательность.
Указатель - это всего лишь одно значение, оно содержит один адрес в памяти.
Именно компилятор знает размер структур и смещения полей в этих структурах. Всякий раз, когда вы обращаетесь к полю в ссылочной структуре, оно добавляет смещение.
Посмотрите на следующую программу:
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
):Вы можете ясно видеть значения 42, 43, 44. Компилятор рассчитал смещения полей в a структура x. они относятся к указателю стека (rbp), поскольку значение x выделяется в стеке.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
Во время объявления структуры (с членом в качестве указателя на тот же тип) требуется только знание о типе, т. е. указатель на тип. Возможно, в этот момент тип не является полным, но указатель на тип известен для этой платформы.
Другими словами, это та же причина, по которой вы не можете иметь член этого типа структуры внутри объявления структуры (в этот момент структура не является полной), но может иметь указатель на тип.Как только вы заставляете элемент указателя указывать на некоторую допустимую память, он также знает начальный адрес. Затем, при разыменовании, он вычисляет адрес смещения и получает значение из этого местоположения, основываясь на ранее упомянутом "типе".
Каждый указатель имеет одинаковый размер в зависимости от вашей системы (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's size int data; // 4 or 8 bytes };
Это приведет к ошибке компиляции.