Ответ 1
Ответ # 2: да, gcc-4 * (я сейчас смотрю v4.3.4, выпущен 4 августа 2009 года, но это должно быть справедливо для всех выпусков gcc-4 на сегодняшний день). Следующее определение используется в их stddef.h:
#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
где __builtin_offsetof
- это компилятор, встроенный как sizeof
(то есть он не реализован как макрос или функция времени исполнения). Компиляция кода:
#include <stddef.h>
struct testcase {
char array[256];
};
int main (void) {
char buffer[offsetof(struct testcase, array[0])];
return 0;
}
приведет к ошибке с использованием расширения макроса, который вы предоставили ( "размер массива" не является интегральным константным выражением), но будет работать при использовании макроса, предоставленного в stddef.h. В сборках с использованием gcc-3 используется макрос, похожий на ваш. Я полагаю, что разработчики gcc были в значительной степени обеспокоены поведением undefined и т.д., Которые были выражены здесь, и создали компилятор в качестве более безопасной альтернативы попытке генерации эквивалентной операции в коде C.
Дополнительная информация:
- A поток списка рассылки из списка разработчиков ядра Linux
- GCC документация на
offsetof
- Сортировка question на этом сайте
Относительно ваших других вопросов: Я думаю, что ответ R и его последующие комментарии хорошо отражают соответствующие разделы стандарта в вопросе № 1. Что касается вашего третьего вопроса, я не слышал о современном компиляторе C, который не имеет stddef.h
. Я, конечно же, не считаю, что какой-либо компилятор не имеет такого базового стандартного заголовка, как "производство". Аналогично, если их реализация offsetof
не работала, то компилятор все еще должен работать, прежде чем его можно считать "производством", как если бы другие вещи в stddef.h(например, NULL
) не работали. Компилятор C, выпущенный до стандартизации C, может не иметь таких вещей, но стандарту ANSI C более 20 лет, поэтому крайне маловероятно, что вы столкнетесь с одним из них.
Вся предпосылка на эти проблемы задает вопрос: если эти люди убеждены, что они не могут доверять версии offsetof
, которую предоставляет компилятор, то чем они могут доверять? Доверяют ли они, что NULL
определено правильно? Доверяют ли они, что long int
не меньше обычного int
? Доверяют ли они, что memcpy
работает так, как предполагалось? Свертывают ли они собственные версии остальных функциональных возможностей библиотеки C? Одной из главных причин наличия языковых стандартов является то, что вы можете доверять компилятору, чтобы делать это правильно. Кажется глупым доверять компилятору для всего остального, кроме offsetof
.
Обновление: (в ответ на ваши комментарии)
Я думаю, что мои коллеги ведут себя так же, как и ваши:-) Некоторые из наших старых кода по-прежнему имеют настраиваемые макросы, определяющие NULL
, VOID
и другие подобные вещи, поскольку "разные компиляторы могут реализовать их по-разному" (вздох). Часть этого кода была написана до того, как C был стандартизирован, и многие старые разработчики все еще находятся в этом мышлении, хотя стандарт C явно говорит об обратном.
Здесь одна вещь, которую вы можете сделать, и доказать, что они ошибаются, и сделать всех счастливыми одновременно:
#include <stddef.h>
#ifndef offsetof
#define offsetof(tp, member) (((char*) &((tp*)0)->member) - (char*)0)
#endif
В действительности, они будут использовать версию, представленную в stddef.h
. Пользовательская версия всегда будет там, однако, если вы столкнетесь с гипотетическим компилятором, который не определяет его.
Основываясь на подобных разговорах, которые у меня были на протяжении многих лет, я думаю, что убеждение, что offsetof
не является частью стандарта C, происходит из двух мест. Во-первых, это редко используется. Разработчики не видят этого очень часто, поэтому они забывают, что он существует. Во-вторых, offsetof
вообще не упоминается в книгах Kernighan и Ritchie "Язык программирования C" (даже самое последнее издание). Первое издание книги было неофициальным стандартом до стандартизации C, и я часто слышу, как люди ошибочно ссылаются на эту книгу как стандарт для языка. Это гораздо легче читать, чем официальный стандарт, поэтому я не знаю, обвиняю ли я их в том, что они сделали его первой точкой отсчета. Независимо от того, что они считают, однако, стандарт ясен, что offsetof
является частью ANSI C (см. Ответ R для ссылки).
Вот еще один способ взглянуть на вопрос №1. стандарт ANSI C дает следующее определение в разделе 4.1.5:
offsetof( type, member-designator)
который расширяется до интегрального постоянного выражения, имеющего тип size_t, значением которого является смещение в байтах, члену структуры (обозначается обозначением участника), с самого начала структура (обозначается типом).
Использование макроса offsetof
не вызывает поведение undefined. Фактически, поведение - это все, что определяет стандарт. Это для писателя компилятора, чтобы определить макрос offsetof
, чтобы его поведение соответствовало стандарту. Вне зависимости от того, реализован ли он с использованием макроса, встроенного компилятора или чего-то другого, гарантируя, что он ведет себя так, как ожидалось, разработчик должен глубоко понимать внутреннюю работу компилятора и как он будет интерпретировать код. Компилятор может реализовать его с помощью макроса, как и идиоматическая версия, которую вы предоставили, но только потому, что они знают, как компилятор будет обрабатывать нестандартный код.
С другой стороны, предоставленное вами макроопределение действительно вызывает поведение undefined. Поскольку вы недостаточно знаете о компиляторе, чтобы предсказать, как он будет обрабатывать код, вы не можете гарантировать, что конкретная реализация offsetof
всегда будет работать. Многие люди определяют свою собственную версию и не сталкиваются с проблемами, но это не значит, что код верен. Даже если так, как определенный компилятор определяет offsetof
, запись этого кода сама вызывает UB при использовании предоставленного макроса offsetof
.
Перемещение собственного макроса для offsetof
невозможно без вызова поведения undefined (раздел ANSI C A.6.2 "Undefined поведение", 27-я точка маркера). Использование stddef.h
версии offsetof
всегда приведет к поведению, определенному в стандарте (при условии, что это соответствует стандартизованному компилятору). Я бы посоветовал не создавать пользовательскую версию, так как это может вызвать проблемы с переносимостью, но если другие не могут быть убеждены, то приведенный выше фрагмент #ifndef offsetof
может быть приемлемым компромиссом.