Почему смещение макроса необходимо?
Я новичок в языке C и только что узнал о структурах и указателях.
Мой вопрос связан с offsetof
макрос я недавно видел. Я знаю, как это работает и какая логика стоит за этим.
в <stddef.h>
подать определение следующим образом:
#define offsetof(type,member) ((unsigned long) &(((type*)0)->member))
Мой вопрос, если у меня есть структура, как показано ниже:
struct test {
int field1:
int field2:
};
struct test var;
Почему я не могу напрямую получить адрес field2
как:
char * p = (char *)&var;
char *addressofField2 = p + sizeof(int);
Вместо того, чтобы писать что-то вроде этого
field2Offset = offsetof (struct test, field2);
а затем добавить значение смещения к начальному адресу VAR?
Есть ли разница? Использует offsetof
более эффективным?
3 ответа
Компилятор C часто добавляет дополнительные биты заполнения или байты между членами struct
чтобы повысить эффективность и сохранить выравнивание целых слов (что в некоторых архитектурах требуется, чтобы избежать ошибок шины, а в некоторых архитектурах требуется, чтобы избежать проблем эффективности). Например, во многих компиляторах, если у вас есть это struct
:
struct ImLikelyPadded {
int x;
char y;
int z;
};
Вы можете найти это sizeof(struct ImLikelyPadded)
12, а не 9, потому что компилятор вставит три дополнительных байта заполнения между концом однобайтового char y
и размером с слово int z
, Вот почему offsetof
Это очень полезно - оно позволяет вам определить, где на самом деле даже учитываются байты заполнения, и является очень переносимым.
В отличие от массивов, структура памяти структуры не всегда смежна. Компилятор может добавить дополнительные байты, чтобы выровнять память. Это называется padding
,
Из-за заполнения нам трудно найти местоположение члена вручную. По этой же причине мы всегда используем sizeof для определения размера структуры.
Макрос Offsetof позволяет узнать расстояние, смещение члена структуры от позиции структуры.
Одно разумное использование, если offsetof замечен в ядре Linux container_of
макро. Этот макрос позволяет узнать начальную позицию узла по адресу члена в общем включающем двусвязном списке
Как уже упоминалось в других ответах, заполнение является одной из причин. Я не буду повторять то, что уже было сказано об этом.
Еще одна веская причина использовать offsetof
Макрос, а не смещение вручную, это то, что вы должны написать его только один раз. Представьте, что произойдет, если вам нужно изменить тип field1
или вставьте или удалите одно или несколько полей перед field2
, Используя ваш ручной расчет, вы должны найти и изменить все его вхождения. Отсутствие одного из них приведет к таинственным ошибкам, которые трудно найти.
Код, написанный с использованием offsetof
не нужно никаких обновлений в этой ситуации. Компилятор позаботится обо всем на следующей компиляции.
Более того, код, который использует offsetof
более понятно. Макрос стандартный, его функциональность документирована. Программист, который читает код, сразу же понимает его. Не так просто понять, что пытается сделать ручной код.