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

发布于 2024-10-09 04:09:06 字数 467 浏览 6 评论 0原文

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

I've got this piece of code. It appears to dereference a null pointer here, but then bitwise-ANDs the result with unsigned int. I really don't understand the whole part. What is it intended to do? Is this a form of pointer arithmetic?

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)));  
}  

The output I get is 44. But how does it work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

守不住的情 2024-10-16 04:09:06

它并不是真正取消引用空指针。您应该查看整个代码。代码的意思是:取数字0,将其视为struct hi *,选择它指向的结构体中的元素b,然后取该元素的地址。此操作的结果将是元素 b 距结构开头的偏移量。当您将其添加到指针时,您将获得等于 4 的元素 b

It isn't really dereferencing the null pointer. You should look on the whole code. What the code says is: take number 0, treat it as struct hi *, select element b in the struct it points to, and take address of this element. The result of this operation would be the offset of the element b from the beginning of the struct. When you add it to the pointer, you get element b which equals to 4.

明媚殇 2024-10-16 04:09:06

这给出了 hi 结构体中 b 字段的字节偏移量

((struct hi *)0) 是一个指向 < code>hi 结构,从地址 0 开始。

(((struct hi *)0)->b) 是上面 struct

& 的 b 字段。 (((struct hi *)0)->b) 是上述字段的地址。由于 hi 结构位于地址 0,因此这是结构内 b 的偏移量。

(无符号整型) & (((struct hi *)0)->b) 是将其从地址类型转换为 unsigned int,以便可以将其用作数字。

您实际上并没有取消引用 NULL 指针。你只是在做指针算术。


访问 (((struct hi *)0)->b) 会给您带来分段错误,因为您正在尝试访问禁止的内存位置。

使用 & (((struct hi *)0)->b) 不会给你分段错误,因为你只获取该禁止内存位置的地址,但你不是尝试访问所述位置。

This gives you the offset in bytes of the b field inside the hi struct

((struct hi *)0) is a pointer to a hi struct, starting at address 0.

(((struct hi *)0)->b) is the b field of the above struct

& (((struct hi *)0)->b) is the address of the above field. Because the hi struct is located at address 0, this is the offset of b within the struct.

(unsigned int) & (((struct hi *)0)->b) is a conversion of that from the address type to unsigned int, so that it can be used as a number.

You're not actually dereferencing a NULL pointer. You're just doing pointer arithmetic.


Accessing (((struct hi *)0)->b) will give you a segmentation fault because you're trying to access a forbidden memory location.

Using & (((struct hi *)0)->b) does not give you segmentation fault because you're only taking the address of that forbidden memory location, but you're not trying to access said location.

旧夏天 2024-10-16 04:09:06

这不是“与”,而是获取右侧参数的地址。
这是一个标准的 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...
希望这是有道理的:)

This is not an "and", this is taking the address of the right hand side argument.
This is a standard hack to get the offset of a struct member at run time. You are casting 0 to a pointer to struct hi, then referencing the 'b' member and getting its address. Then you add this offset to the pointer "ptr" and getting real address of the 'b' field of the struct pointed to by ptr, which is ob. Then you cast that pointer back to int pointer (because b is int) and output it.
This is the 2nd print.
The first print outputs num, which is 4 not because b's value is 4, but because 4 is the offset of the b field in hi struct. Which is sizeof(int), because b follows a, and a is int...
Hope this makes sense :)

甜是你 2024-10-16 04:09:06

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

第一个表达式 - for num - 是 offsetof 宏的常见实现;它不便于携带,但通常可以工作。

第二个表达式将其与 0(空指针)相加,并给出相同的答案 - 4。

第二个表达式将 4 添加到 ptr 指向的对象的基地址上,即结构中的值 4。

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

You must be using a 32-bit compile (or a 64-bit compile on Windows).

The first expression - for num - is a common implementation of the offsetof macro from <stddef.h>; it is not portable, but it often works.

The second expression adds that to 0 (the null pointer) and gives you the same answer - 4.

The second expression adds 4 to the base address of the object that ptr points to, and that is the value 4 in the structure.

Your output does not include a newline - it probably should (the behaviour is not completely portable because it is implementation defined if you don't include the newline: C99 §7.19.2: "Whether the last line requires a terminating new-line character is implementation-defined."). On a Unix box, it is messy because the next prompt will appear immediately after the 44.

维持三分热 2024-10-16 04:09:06

只是为了澄清您必须了解 NULL 指针取消引用和何时不将其视为取消引用之间的区别。该规范实际上规定取消引用不会发生,并且当您拥有 & 时实际上会被优化掉。表达式中的 (address-of) 运算符。

所以 &((struct T*)0)->b) 实际上优化了 ->;并从偏移量 0 跳转该字节数并假设它是一个 struct T *。对于新手来说,这确实让事情变得混乱。然而,它在 Linux 内核中被广泛使用 - 并提供了 list_entry、list_head 和各种新手无法理解的指针算术魔法的实际意义。

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

要了解更多信息 - 您可以在 Robert Love 的书“Linux Kernel Development”中阅读相关内容。

Just to clarify that you must understand the difference between NULL-pointer dereference and when it's not considered a de-reference. The spec actually dictates that the de-reference does not happen, and is actually optimised away when you have the & (address-of) operator in the expression.

So the &((struct T*)0)->b) actually optimises out the -> and just jumps that number of bytes from offset 0 and assumes it's a struct T *. This really obfuscates things for new beginners. However, it's widely used in the Linux Kernel - and provides an actual sense of list_entry, list_head's and various pointer arithmetic magic that newbies can't comprehend.

In any event, it's a programmatic way of finding the offset of 'b' within the struct T object. It's used in offsetof as well as other list_head operations such as list_entry.

For more information - you can read about this within Robert Love's Book titled "Linux Kernel Development".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文