37

在下面的一段代码中,是什么*(int32 *) 0 = 0;意思?

void
function (void)
{
  ...

  for (;;)
     *(int32 *) 0 = 0;     /* What does this line do? */
}

几点注意事项:

  • 该代码似乎无法访问,因为在该特定代码之前有一个退出语句。
  • int32typedef'ed,但你不应该太在意它。
  • 对于任何感兴趣的人,这段代码来自编译器中的语言运行时。
4

8 回答 8

33

该代码正在执行以下操作:

   for (;;) // while(true)
     *(int32 *) 0 = 0; // Treat 0 as an address, de-reference the 0 address and try and store 0 into it.

这应该是段错误,空指针取消引用。

编辑

编译并运行以获取更多信息:

#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;
于 2013-08-23T16:20:26.083 回答
26

由于 OP 声明代码是由经验丰富的编译器工程师编写的,因此这可能是代码的意图:

  • *(int32 *) 0 = 0;此特定 C 实现将其识别为导致 C 标准未定义且此实现已知的行为非法的代码。
  • 另外for (;;)表示此代码永远不会退出。
  • 编译器工程师知道优化器会识别这段代码并推断它可能被“优化掉”,因为任何到达这段代码的程序都被允许有任何行为,所以优化器可以选择给它的行为就像代码一样永远达不到。1

只有当您对 C 实现的内部操作有特定的了解时,这种推理才有可能。这是编译器工程师可能会在 C 实现的特殊头文件中包含的那种东西,也许是为了标记某些代码(例如abort调用后的代码)永远不会到达。它不应该在正常编程中使用。


1例如,考虑以下代码:

if (a)
    for (;;)
        *(int 32 *) 0 = 0;
else
    foo();

编译器可以识别 then 子句允许有任何行为。因此,编译器可以自由选择它具有的行为。为简单起见,它选择它具有与 相同的行为foo();。那么代码就变成了:

if (a)
    foo();
else
    foo();

并可进一步简化为:

foo();
于 2013-08-23T17:56:34.360 回答
21

事实上,这段代码段错误并不能解释它为什么存在=)

我认为这是来自某些 MCU 的运行时 .. 之所以存在,是因为如果程序执行到这一点,则此类指令将启动 MCU 的软件复位,因此程序将重新启动(这是嵌入式开发中的常见做法)或如果 MCU 配置了硬件看门狗,由于硬件看门狗和永不结束的循环,强制 MCU 重启。

这种构造的主要目标是调用可由操作系统或硬件处理的中断以启动某些操作。

知道它的 x86 它将取决于 CPU 模式......在实模式下,如果没有看门狗,什么都不会立即发生,在地址 0 处有一个“除以 0”处理程序的地址,所以如果它是一些旧的 MS- DOS 或嵌入式 x86 运行时它会将“除以 0”处理程序的地址更改为 0,因此一旦发生并且此中断未被屏蔽,CPU 将跳转到位置 0:0,并且可能由于非法指令而重新启动..如果它是受保护的或VM x86代码,那么这是一种通知操作系统或任何其他主管运行时出现问题并且软件应该在外部“杀死”的方法。

于 2013-08-23T16:56:30.833 回答
6

for(;;)相当于while(1),

*(int32 *) 0 = 0;将 0 写入取消引用的空指针,这预计会导致崩溃,但实际上在某些编译器上始终不会:Crashing threads with *(int*)NULL = 1; 有问题?

于 2013-08-23T16:21:51.793 回答
3

这是未定义行为的无限循环(取消引用空指针)。它可能会因 *n*x 上的段错误或 Windows 上的访问冲突而崩溃。

于 2013-08-23T16:20:36.230 回答
3

Mike's comment is pretty well correct: it's storing the VALUE zero at the ADDRESS 0.

Which will be a crash on most machines.

于 2013-08-23T16:29:29.160 回答
3

最初的IBM PC 将中断向量表存储在最低 1 KiB 的内存中。因此,在这种架构上实际上将 32 位值写入地址 0 会覆盖INT 00h. INT 00h 在 PC 中看起来未使用。

在基本上任何现代的东西上(在 x86/x86-64 中意味着任何在受保护模式下运行的东西),除非您处于环 0(内核模式),否则它将触发分段错误,因为您正在超出您的进程允许的地址取消引用范围。

由于取消引用是未定义的行为(如前所述),因此分段错误是处理这种情况的完全可接受的方式。如果您知道在目标架构上零地址取消引用会导致分段错误,那么这似乎是让应用程序崩溃的一种非常可靠的方法。如果exit()返回,那可能就是您想要做的,因为某些事情发生了可怕的错误。代码来自特定编译器的运行时意味着编写它的人可以利用编译器和运行时内部工作的知识,并根据特定目标架构的行为对其进行调整。

于 2013-08-23T23:35:09.400 回答
1

可能是编译器不知道exit()不会返回,但它确实知道此构造不会返回。

于 2013-08-23T18:47:20.537 回答