在C中明显的NULL指针取消引用实际上是指针算术吗?

我有这段代码。它似乎在这里取消引用空指针,但随后用
unsigned int
对结果进行按位与运算。我真的不明白整个部分。它打算做什么?这是指针算术的一种形式吗?
struct hi  
{
   long a;  
   int b;  
   long c;  
};  

int main()  
{  
    struct hi ob={3,4,5};  
    struct hi *ptr=&ob;  
    int num= (unsigned int) & (((struct hi *)0)->b);  

   printf("%d",num);  
   printf("%d",*(int *)((char *)ptr + (unsigned int) & (((struct hi *)0)->b)));  
}  
我得到的输出是44.但它是如何工作的?     
已邀请:
这不是“和”,这是取右手边的论点。 这是在运行时获取struct成员的偏移量的标准hack。您将0转换为指向struct hi的指针,然后引用'b'成员并获取其地址。然后将此偏移量添加到指针“ptr”并获取ptr指向的结构的“b”字段的实际地址,即ob。然后将该指针强制转换回int指针(因为b为int)并输出它。 这是第二次印刷。 第一个打印输出num,其中4不是因为b的值是4,而是因为4是hi结构中b字段的偏移量。这是sizeof(int),因为b跟a,a是int ... 希望这有道理:)     
它实际上并不是取消引用空指针。你应该看看整个代码。代码说的是:取数字
0
,将其视为
struct hi *
,在它指向的结构中选择元素
b
,并获取该元素的地址。此操作的结果将是元素
b
从结构的开头偏移。当你将它添加到指针时,你得到的元素
b
等于
4
。     
这为您提供了
hi
结构内
b
字段的偏移量
((struct hi *)0)
是指向
hi
结构的指针,从地址
0
开始。
(((struct hi *)0)->b)
是上述结构的
b
字段
& (((struct hi *)0)->b)
是上述字段的地址。因为
hi
结构位于地址
0
,这是结构中
b
的偏移量。
(unsigned int) & (((struct hi *)0)->b)
是从地址类型到
unsigned int
的转换,因此可以用作数字。 你实际上并没有取消引用一个
NULL
指针。你只是做指针算术。 访问
(((struct hi *)0)->b)
会给您一个分段错误,因为您正在尝试访问禁止的内存位置。 使用
& (((struct hi *)0)->b)
不会给你分段错误,因为你只是获取该禁止内存位置的地址,但你并没有尝试访问所述位置。     
您必须使用32位编译(或Windows上的64位编译)。 第一个表达式 - 对于
num
- 是
<stddef.h>
offsetof
宏的常见实现;它不便携,但它经常有效。 第二个表达式将其添加到0(空指针)并给出相同的答案 - 4。 第二个表达式将
ptr
指向的对象的基地址加4,即结构中的值4。 您的输出不包含换行符 - 它可能应该(行为不是完全可移植的,因为如果您不包含换行符,则它是实现定义的:C99§7.19.2:“最后一行是否需要终止换行符是实现定义的。“)。在Unix机器上,它很乱,因为下一个提示将在44之后立即出现。     
只是为了澄清你必须理解NULL指针解除引用和它不被视为去引用之间的区别。该规范实际上规定了de-reference不会发生,并且当你拥有&amp;表达式中的(address-of)运算符。 所以&amp;((struct T *)0) - > b)实际上优化了 - >并且只是从偏移量0跳过那个字节数并假设它是一个struct T *。这对初学者来说真的很模糊。但是,它在Linux内核中被广泛使用 - 并提供了list_entry,list_head和各种指针算术魔法的实际意义,新手无法理解。 无论如何,它是一种在struct T对象中找到'b'偏移的编程方式。它用于offsetof以及list_head等其他list_head操作。 有关更多信息,您可以在Robert Love的书籍“Linux Kernel Development”中阅读此内容。     

要回复问题请先登录注册