9

我在一本书中找到了这个代码示例,但我无法理解 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)));
}
4

3 回答 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(as name.b) 从 NULL (0) 的偏移量,它是 4 个字节(假设 along是 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 因为它使用相同的偏移量来获取值。

于 2014-11-13T09:22:27.447 回答
4

编码:

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

struct name旨在获取距离变量开头有多远的计数(以字节为单位b)。

有一个标准的方法来做到这一点:offsetof(struct name, b); . 编写此代码的人要么不知道offsetof,要么正试图教一些东西(尽管这可能是盲人引导盲人的情况)。

该代码通过取消引用空指针导致未定义的行为,但是普通编译器可能会在不触发错误的情况下接受它,这可能是因为编译器开发人员知道存在这样的现有代码。


其余的代码很简单;它指向结构的开始;前进这么多字节,并从该位置读取一个 int ;这当然和直接阅读是一样的b

于 2014-11-13T09:38:30.980 回答
4

re是类型的局部变量r,即 a struct name;它通常分配在调用堆栈上。

na是指向 的指针re

(unsigned int) & (((struct name *)NULL)->b)可能是未定义的行为(但我不确定),但大多数编译器会将其编译为字段的偏移量 - 以字节为单位b(就像offsetof确实如此,请参阅offsetof(3))。在我的机器上可能是 8。

(char*)na + 上面的偏移量通常与地址相同&re.b

您取消引用该指针,实际上是&re.b

我觉得您的代码可能不符合标准(有关一些论点,请参阅此答案;可能存在假设的机器和 C 实现,其中NULL不是全零位字,我不知道这样的实现),但在我知道的所有机器上关于,它应该打印字段的值re.b

于 2014-11-13T09:18:23.317 回答