我在一本书中找到了这个代码示例,但是我无法理解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)));
}
答案 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)
re
是r
类型的局部变量,即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
相同。