Что делает ((имя структуры *) 0) → член) в C?
Фактический оператор C, с которым я столкнулся, был следующим:
(char *) &((struct name *)0)->member)
Что делает ((имя структуры *) 0) → член) в C?
Фактический оператор C, с которым я столкнулся, был следующим:
(char *) &((struct name *)0)->member)
Это трюк для получения смещения члена struct
, называемого member
. Идея этого подхода заключается в том, чтобы компилятор вычислил адрес member
, предполагая, что сама структура находится по адресу нуль.
Использование offsetof
предлагает более читаемую альтернативу этому синтаксису:
size_t offset = offsetof(struct name, member);
(struct name *)0
отличает 0
указателем на struct name
. &((struct name *)0)->member)
получает адрес члена member
. (char *) &((struct name *)0)->member)
отправляет этот адрес на char *
.
Если кто-то считает, что указанное выше выражение разыменовывает указатель NULL
, то обратите внимание, что здесь не существует разыменования. Все это для получения адреса члена number
.
(struct name*)0
дает указатель на структуру.
((struct name*)0)->member
дает вам член структуры, на который указывает указатель.
Добавление &
дает вам адрес этого члена и, наконец,
(char *)
- передать полученный адрес указателю char.
Во многих компиляторах приведенное выше выражение даст char*
, который, хотя он не является допустимым указателем, имеет одно или оба из следующих свойств:
Приведение указателя непосредственно к целочисленному типу приведет к смещению указанного элемента внутри структуры.
Вычитание (char*)0
из указателя приведет к смещению указанного элемента внутри структуры.
Обратите внимание, что стандарт C не предъявляет никаких требований относительно того, что может произойти, если код формирует недопустимое значение указателя с помощью вышеуказанных средств или любого другого, даже если код не пытается разыменовать указатель. Хотя многие компиляторы производят указатели с указанными качествами, такое поведение не является универсальным. Даже на платформах, где существует четкая естественная связь между указателями и целыми числами, некоторые производители компиляторов могут решить, что, если программы ведут себя так, как подразумевается такими отношениями, было бы более "эффективно" вместо этого, чтобы компилятор предполагал, что программа никогда не будет получать входные данные, которые приведут к генерации недействительных указателей и, следовательно, следует исключить любой код, который будет иметь значение только в том случае, если такие входы были опущены.