&((struct name *)NULL - > b)在printf语句中

时间:2014-11-13 09:10:56

标签: c struct casting offsetof

我在一本书中找到了这个代码示例,但是我无法理解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 个答案:

答案 0 :(得分: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

所以,*(int*)((char*)na + 2(或者可能是1,或者可能是4,取决于机器)。

其余的很简单:re->b将指向r re ={3,4,5};所以它应该打印4 (代码中已初始化的内容(unsigned int ) & (( (struct name *)NULL)->b ) != 2)。

P.S:即使{{1}}(也许是它的1,4或8) - 它仍然应该打印4,因为它然后使用相同的偏移来获得该值。

答案 1 :(得分:4)

rer类型的局部变量,即struct name;它通常分配在call stack

na是指向re的指针。

(unsigned int) & (((struct name *)NULL)->b)可能是undefined behavior(但我不确定),但大多数编译器会将其编译为字段b的偏移量-in字节 - (如offsetof的确如此,见offsetof(3))。在我的机器上可能是8。

(char*)na +上述偏移通常与&re.b

的地址相同

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

我认为您的代码可能不符合标准(请参阅this answer进行一些论证;可能存在假设的机器和C实现,其中NULL不是一个全零位的字,I知道没有这样的实现),但在我所知道的所有机器上,它应该打印字段re.b的值

答案 2 :(得分:4)

代码:

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

旨在获取变量struct name的{​​{1}}开始距离的计数(以字节为单位)。

有一种标准方法可以执行此操作:b。编写此代码的人要么不知道offsetof(struct name, b);,要么试图教某些东西(虽然可能是盲人领导盲人的情况)。

代码通过解除引用空指针导致未定义的行为,但是常见的编译器可能会接受它而不会触发错误,这可能是因为编译器开发人员知道存在这样的现有代码。


其余代码很简单;它指向结构的开始;通过那么多字节前进,并从该位置读取一个int;这当然与直接阅读offsetof相同。