&((имя структуры *)NULL -> b) в выражении printf
Я нашел этот пример кода в книге, но не могу понять выражение в выражении printf. и эта программа успешно компилирует, выдавая вывод 4. любезно советую...
void main(){
unsigned char c;
typedef struct name {
long a;
int b;
long c;
}r;
r re = {3,4,5};
r *na=&re;
printf("%d",*(int*)((char*)na + (unsigned int ) & (((struct name *)NULL)->b)));
}
3 ответа
Начнем с последней строки:
printf("%d",*(int*)((char*)na + (unsigned int ) & (((struct name *)NULL)->b)));
Давайте интерпретировать:
(unsigned int ) & (( (struct name *)NULL)->b )
На самом деле кастинг & (( (struct name *)NULL)->b )
в unsigned int
,
& (( (struct name *)NULL)->b )
это адрес (т.е. он дает указатель на):
(( (struct name *)NULL)->b )
Что на самом деле является смещением b
(как name.b
) из NULL (0), что составляет 4 байта (при условии long
4 байта) и конвертируется в указатель типа int, дает 2 (при условии int
2 байта).
Если вместо NULL
это был бы указатель на 0xFFFF0000
, затем &(ptr->b)
был бы 0xFFFF0002
, Но это больше похоже на &(0 -> b)
так что это 0x00000002
,
Так, (unsigned int ) & (( (struct name *)NULL)->b ) == 2
(или может быть 1, или, может быть, 4, в зависимости от машины).
Остальное просто: *(int*)((char*)na + 2
будет указывать на re->b
, Таким образом, он должен вывести 4 (что было инициализировано в коде, r re ={3,4,5};
).
PS: даже если (unsigned int ) & (( (struct name *)NULL)->b ) != 2
(возможно, это 1, 4 или 8) - он должен все еще печатать 4, потому что тогда он использует то же смещение для получения значения.
re
является локальной переменной типа r
т.е. struct name
; обычно он размещается в стеке вызовов.
na
это указатель на re
,
(unsigned int) & (((struct name *)NULL)->b)
может быть неопределенным поведением (но я не уверен), но большинство компиляторов компилируют это в поле смещения -in bytes- of b
(лайк offsetof
делает, см. offsetof (3)). На моей машине это может быть 8.
(char*)na +
вышеуказанное смещение часто совпадает с адресом &re.b
Вы разыменовываете тот указатель, который практически &re.b
Я чувствую, что ваш код может не соответствовать стандарту (см. Этот ответ для некоторых аргументов; могут быть гипотетические реализации машин и C, где NULL
это не слово с нулевыми битами, я не знаю таких реализаций), но на всех машинах, о которых я знаю, должно выводиться значение поля re.b
Код:
(unsigned int ) & (((struct name *)NULL)->b))
предназначен для получения подсчета в байтах того, как далеко от начала struct name
переменная b
является.
Существует стандартный способ сделать это: offsetof(struct name, b);
, Человек, который написал этот код либо не знал offsetof
или пытался чему-то научить (хотя это может быть случай слепого, ведущего слепого).
Код вызывает неопределенное поведение, разыменовывая нулевой указатель, однако обычные компиляторы могут принять его, не вызывая ошибки, возможно потому, что разработчики компилятора знают, что такой код существует.
Остальная часть кода проста; это указывает на начало структуры; продвигается на столько байтов и читает int из этого местоположения; что, конечно, так же, как просто чтение b
непосредственно.