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

时间:2010-12-26 04:49:00

标签: c struct pointer-arithmetic null-pointer offsetof

我有这段代码。它似乎在这里取消引用空指针,但随后将结果与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.但它是如何工作的?

5 个答案:

答案 0 :(得分:8)

它实际上并不是解除引用空指针。你应该看看整个代码。代码说的是:取数字0,将其视为struct hi *,在其指向的结构中选择元素b,并获取此元素的地址。此操作的结果将是元素b从结构的开头偏移。将其添加到指针时,元素b等于4

答案 1 :(得分:6)

这为您提供了b struct

hi字段的偏移量(以字节为单位)

((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)并不会给您带来分段错误,因为您只获取了该禁止内存位置的地址,但您并未尝试访问说位置。

答案 2 :(得分:3)

您必须使用32位编译(或Windows上的64位编译)。

num的第一个表达式是来自offsetof的{​​{1}}宏的常见实现;它不便于携带,但通常可以使用。

第二个表达式将其添加到0(空指针)并给出相同的答案 - 4。 第二个表达式将<stddef.h>指向的对象的基地址加4,并且结构中的值为4。

您的输出不包含换行符 - 它可能应该(行为不是完全可移植的,因为如果您不包含换行符,它是实现定义的:C99§7.19.2:“最后一行是否需要终止新行-line字符是实现定义的。“)。在Unix机器上,它很乱,因为下一个提示将立即出现在44。

之后

答案 3 :(得分:3)

这不是“和”,这是取右手边的论点。
这是在运行时获取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 ... 希望这是有道理的:)

答案 4 :(得分:0)

只是为了澄清你必须理解NULL指针取消引用和它不被视为去引用之间的区别。该规范实际上规定了de-reference不会发生,并且当你拥有&amp;表达式中的(address-of)运算符。

所以&amp;((struct T *)0) - &gt; b)实际上优化了 - &gt;并且只是从偏移0跳过那个字节数并假设它是一个struct T *。这对初学者来说真的很模糊。但是,它在Linux内核中被广泛使用 - 并且提供了list_entry,list_head和新手无法理解的各种指针算术魔法的实际意义。

无论如何,它是一种以编程方式查找struct T对象中'b'的偏移量。它用于offsetof以及其他list_head操作,例如list_entry。

有关更多信息,您可以在Robert Love的书籍“Linux Kernel Development”中阅读此内容。

相关问题