Чтобы ответить на № 2: да, gcc-4 * (сейчас я смотрю на версию 4.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;
}
приведет к ошибке при использовании расширения предоставленного вами макроса («размер массива« buffer »не является интегральным константным выражением»), но будет работать при использовании макроса, предоставленного в stddef.h. В сборках с использованием gcc-3 использовался макрос, похожий на ваш. Я полагаю, что у разработчиков gcc были многие из тех же проблем, которые были выражены здесь в отношении неопределенного поведения и т. Д., И они создали встроенный компилятор как более безопасную альтернативу попытке сгенерировать эквивалентную операцию в коде C.
Дополнительная информация:
Что касается других ваших вопросов: я думаю, что ответ R и его последующие комментарии хорошо справляются с изложением соответствующих разделов стандарта в том, что касается вопроса № 1. Что касается вашего третьего вопроса, я не слышал о современном компиляторе C, в котором не было бы stddef.h
. Я бы точно не стал рассматривать какой-либо компилятор без такого базового стандартного заголовка, как "production". Точно так же, если их 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
вообще не упоминается в основополагающей книге Кернигана и Ричи "Язык программирования C" (даже в самой последнее издание). Первое издание книги было неофициальным стандартом до того, как был стандартизован Си, и я часто слышу, как люди ошибочно называют эту книгу ЭТОМ стандартом для языка. Его гораздо легче читать, чем официальный стандарт, поэтому я не знаю, виню ли я их за то, что они сделали его своей первой точкой отсчета. Однако независимо от того, во что они верят, стандарт ясно, что offsetof
является частью ANSI C (ссылку см. В ответе R).
Вот еще один способ взглянуть на вопрос №1. стандарт ANSI C дает следующее определение в разделе 4.1.5:
offsetof( type, member-designator)
который расширяется до целочисленного константного выражения, имеющего тип size_t, значение которого является смещением в байтах, до элемента структуры (обозначенного указателем-членом) от начала его структуры (обозначенного типом).
Использование макроса offsetof
не вызывает неопределенного поведения. Фактически, поведение - это все, что фактически определяет стандарт. Создатель компилятора должен определить макрос offsetof
таким образом, чтобы его поведение соответствовало стандарту. Независимо от того, реализовано ли оно с использованием макроса, встроенного компилятора или чего-то еще, обеспечение его правильного поведения требует от разработчика глубокого понимания внутренней работы компилятора и того, как он будет интерпретировать код. Компилятор может реализовать это с помощью макроса, такого как идиоматическая версия, которую вы предоставили, но только потому, что они знают, как компилятор будет обрабатывать нестандартный код.
С другой стороны, предоставленное вами расширение макроса действительно вызывает неопределенное поведение. Поскольку вы недостаточно знаете о компиляторе, чтобы предсказать, как он будет обрабатывать код, вы не можете гарантировать, что конкретная реализация offsetof
всегда будет работать. Многие люди так определяют свою собственную версию и не сталкиваются с проблемами, но это не значит, что код правильный. Даже если именно так конкретный компилятор определяет offsetof
, написание этого кода самостоятельно вызывает UB, а использование предоставленного макроса offsetof
- нет.
Сворачивание вашего собственного макроса для offsetof
не может быть выполнено без вызова неопределенного поведения (ANSI C раздел A.6.2 «Неопределенное поведение», 27-й маркер). Использование версии offsetof
для stddef.h
всегда будет приводить к поведению, определенному в стандарте (при условии, что компилятор соответствует стандартам). Я бы не советовал определять собственную версию, поскольку это может вызвать проблемы с переносимостью, но если других не удается убедить, то приведенный выше фрагмент #ifndef offsetof
может быть приемлемым компромиссом.
person
bta
schedule
15.07.2011
->
к нулевому указателю. (Кроме того, вы обычно кажетесь более озабоченными компиляторами в прошлом, чем в будущем, когда последних будет бесконечно больше.) - person Nemo   schedule 15.07.2011offsetof
. - person R.. GitHub STOP HELPING ICE   schedule 15.07.2011