2014-11-13 6 views
9

Я нашел этот образец кода в книге, но я не могу понять выражение в выражении printf. и эта программа компилируется успешно дает выход в 4. любезно посоветует ...& ((struct name *) NULL -> b) in printf statement

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))); 
} 
+1

Вы знаете, что 'main' имеет возвращаемый тип' int' в C (и C++)? – Deduplicator

+0

Эта точная программа была опубликована 3 года назад, я думаю, это обычная книга! –

ответ

9

Начнём с последней строки:

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};).

P.S: даже если (unsigned int) & (((struct name *)NULL)->b) != 2 (возможно, это 1, 4 или 8) - он все равно должен печатать 4, потому что тогда он использует то же смещение, чтобы получить значение.

+1

Помните, что эта конструкция вызывает UB. – Deduplicator

+5

@Deduplicator Is '(((имя структуры *) NULL) -> b)' undefined behavior? Это на самом деле кажется мне довольно определенным. Можете ли вы указать мне ссылку, которая указывает это? –

+1

@MarkSegal отличное объяснение ... большое спасибо ... –

4

re локальная переменная типа r, т.е. struct name; он обычно выделяется на call stack.

na является указателем на re.

(unsigned int) & (((struct name *)NULL)->b) может быть undefined behavior (но я не уверен), но большинство компиляторов будет компилировать, что смещение -in bytes- поля b (как offsetof делает, см offsetof(3)). На моей машине, которая может быть 8.

(char*)na + выше смещение часто тот же адрес, &re.b

Вы разыменования, что указатель, который практически является &re.b

Я чувствую, что ваш код не может быть совместим со стандартом (см. this answer для некоторых аргументов: могут быть гипотетические машины & Реализации C, где NULL - это слово без нуля, я не знаю таких реализаций), но на всех машинах, о которых я знаю, он должен печатать значение поля re.b

+0

спасибо @Basile .... давайте придерживаться машин, о которых мы знаем тогда ... :) –

+1

@HimanshuSourav: http://c-faq.com/null/machexamp.html Просто чтобы вы знали еще несколько машин ;-) – Deduplicator

+1

@ Дедупликатор: но эти старые машины XX века существуют только в музеях! –

4

Код:

(unsigned int) & (((struct name *)NULL)->b)) 

предназначен для получения счета, в байтах, как далеко от начала в struct name переменной b есть.

Существует стандартный способ для этого: offsetof(struct name, b);. Человек, который написал этот код, либо не знал offsetof, либо пытался что-то научить (хотя это может быть случай слепых, ведущих слепых).

Код вызывает undefined behavoiur путем разыменования нулевого указателя, однако распространенные компиляторы могут принять его без возникновения ошибки, возможно, потому, что разработчики компилятора знают, что там существует существующий код.


Остальная часть кода проста; он указывает на начало структуры; продвигается этим количеством байтов и считывает int из этого местоположения; что, конечно, то же самое, что и прямое чтение b.

+1

Технически код * не * отменяет указатель 'NULL'. Но я все еще чувствую, что это неопределенное поведение. –

+1

@BasileStarynkevitch Я начал новый вопрос, действительно ли это UB, хотя при первом чтении C11 он выглядит не очень хорошо –