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

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

 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 offsetof宏的常见实现; 它不便携,但它经常有效。

第二个表达式将其添加到0(空指针)并给出相同的答案 – 4.第二个表达式将4添加到ptr指向的对象的基地址,这是结构中的值4。

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

只是为了澄清你必须理解NULL指针取消引用之间的区别以及它何时不被视为去引用。 规范实际上规定取消引用不会发生,并且当表达式中包含&(address-of)运算符时,实际上会对其进行优化。

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

无论如何,它是一种在struct T对象中找到’b’偏移的编程方式。 它用于offsetof以及list_head等其他list_head操作。

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