在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内核开发”中阅读此内容。