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