这是什么意思?:*(int32 *)0 = 0;
在下面的一段代码中, *(int32 *) 0 = 0;
意思?
void function (void) { ... for (;;) *(int32 *) 0 = 0; /* What does this line do? */ }
一些注意事项:
- 代码似乎不可达,因为在特定代码之前有一个exit语句。
-
int32
是typedef
的,但你不应该太在意它。 - 这段代码来自编译器中的语言运行时,对于任何感兴趣的人。
代码正在执行以下操作:
for (;;) // while(true) *(int32 *) 0 = 0; // Treat 0 as an address, de-reference the 0 address and try and store 0 into it.
这应该segfault,空指针去引用。
编辑
编译并运行以获取更多信息:
#include <stdio.h> #include <stdlib.h> #include <stdint.h> int main(void){ *(int32_t *) 0 = 0; printf("done\n"); return 0; }
gcc -g null.c; ./a.out
Program received signal SIGSEGV, Segmentation fault. 0x00000000004004cd in main () at null.c:7 7 *(int32_t *) 0 = 0;
由于OP声明代码是由经验丰富的编译器工程师编写的,因此这可能是代码的意图:
-
*(int32 *) 0 = 0;
被这个特定的C实现认可为导致未被C标准定义的行为并且该实现已知为非法的行为的代码。 -
for (;;)
另外表示这个代码永远不会退出。 - 编译器工程师知道,优化器会识别这个代码,并推断它可能被“优化了”,因为任何到达这个代码的程序都被允许有任何行为,所以优化器可以select赋予其行为,就好像代码永远不会到达。 1
只有当你具体了解C实现的内部操作时,这种推理才是可能的。 这是编译器工程师可能在C实现的特殊头文件中包含的东西,也许是为了标记某些代码(例如在abort
调用之后的代码)永远不会到达。 它不应该在正常的编程中使用。
1例如,考虑这个代码:
if (a) for (;;) *(int 32 *) 0 = 0; else foo();
编译器可以认识到then子句被允许有任何行为。 因此,编译器可以自由select它具有的行为。 为了简单起见,它select它具有与foo();
相同的行为foo();
。 然后代码变成:
if (a) foo(); else foo();
并可以进一步简化为:
foo();
事实上,这段代码段错误并不能解释为什么它存在=)
我认为这是来自某个MCU的运行时间,因为如果程序执行到了这一点,这个指令要么启动一个MCU的软件复位,所以程序将被重新启动(这在embedded式开发中是常见的)或者如果MCUconfiguration了硬件看门狗,强制MCU由于硬件看门狗而重启,并且永不结束循环。
这种结构的主要目的是调用一个中断,这个中断可以由操作系统或硬件来处理,以启动某些操作。
知道它的x86将取决于CPU模式…在实模式下,如果没有看门狗,没有真正的事情发生,在地址0有一个地址'除以0'处理程序,所以如果它是一些旧的MS- DOS或embedded式x86运行时,会将“0除以0”处理程序的地址更改为0,因此一旦发生此中断,屏蔽程序将不会屏蔽CPU将跳转到位置0:0,并可能因为非法指令..如果它受保护或虚拟机x86代码,那么这是一种方式来通知操作系统或任何其他主pipe,在运行时出现问题,应该从外部'杀死'软件。
for(;;)
相当于while(1)
,
*(int32 *) 0 = 0;
写0到一个解除引用的空指针,这是预期会导致崩溃,但实际上不会在任何时候在某些编译器: 崩溃的线程*(int *)NULL = 1; 有问题?
这是一个未定义行为的无限循环(解引用空指针)。 它很可能会在Windows *上的段错误或Windows上的访问冲突中崩溃。
Mike的评论是非常正确的:它将0值存储在地址0。
这将是大多数机器崩溃。
最初的IBM PC将中断向量表存储在最低1 KiB的内存中 。 因此,在这样的体系结构中,实际上将32位值写入地址0将会覆盖INT 00h
的地址。 INT 00h在个人电脑中看起来没有用处。
对于基本上任何现代的东西(意思是在x86 / x86-64中放置任何运行在受保护模式或长模式下的东西),除非你处于环0(内核模式),否则将触发分段错误,因为你超出了你的进程允许的地址解引用范围。
由于解引用是未定义的行为(如前所述),分段错误是处理这种情况的完全可接受的方式。 如果你知道在目标体系结构中零地址解引用会导致分段错误,那么这似乎是一个非常可靠的方法来让应用程序崩溃。 如果exit()
返回,那可能是你想要做的事情,因为某些事情可怕的是错误的。 代码来自特定编译器的运行时,意味着编写代码的人可以利用编译器和运行时的内部工作知识,以及针对特定目标架构的行为进行调整。
这可能是编译器不知道exit()
不返回,但它知道这个构造不返回。