#define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT))
очень похоже на довольно распространенное определение стандартного макроса offsetof()
, определенного в <stddef.h>
(в C) или <cstddef>
(в C ++).
0
is константа нулевого указателя .Приведение его к TYPE *
дает нулевой указатель типа TYPE *
.Обратите внимание, что язык не гарантирует (и даже не подразумевает), что нулевой указатель имеет значение 0, хотя это очень часто встречается.
Так что (TYPE *)0
- это условно адрес объекта типа TYPE
находится по любому адресу, на который указывает нулевой указатель, а ((TYPE *)0)->ELEMENT))
является членом ELEMENT
этого объекта.
Оператор &
получает адрес этого члена ELEMENT
, и преобразование преобразует этоадрес для типа size_t
.
Теперь , если пустой указатель указывает на адрес 0, тогда (несуществующий) объект типа TYPE
начинается с адреса 0, иадрес члена ELEMENT
этого объекта находится по адресу, который смещен на некоторое количество байтов от адреса 0. Предполагается, что преобразование, определенное реализацией из TYPE *
в size_t
, ведет себя прямым образом (что-то еще, что негарантируется языком), результатом всего выражения будет смещение элемента ELEMENT
в объекте типа TYPE
.
All tон зависит от нескольких неопределенных или неопределенных форм поведения.В большинстве современных систем нулевой указатель реализован в виде указателя на адрес 0, адреса (значения указателя) представляются так, как если бы они были целыми числами, определяющими индекс конкретного байта в монолитном адресном пространстве, и преобразуют указатель в целое число.одного и того же размера просто переосмысливает биты.В системе с такими характеристиками макрос OFFSETOF
, скорее всего, будет работать, и реализация может использовать аналогичное определение для стандартного макроса offsetof
.(Код, являющийся частью реализации, может использовать преимущества поведения, определенного или неопределенного поведения; не обязательно быть переносимым.)
В системах, которые не имеют этих характеристик, этот макрос OFFSETOF
может не работать- и реализация должна использовать какой-то другой метод для реализации offsetof
.Вот почему offsetof
является частью стандартной библиотеки;он не может быть реализован переносимо, но он всегда может быть реализован некоторым способом для любой системы.И некоторые реализации используют магию компилятора, например, gcc __builtin_offsetof
.
На практике не имеет особого смысла определять свой собственный макрос OFFSETOF
, как это, так как любая соответствующая реализация на C или C ++ обеспечитрабочий макрос offsetof
в стандартной библиотеке.