这行代码的含义是什么 *((int*)(0))=1;?

14

所以问题中的代码行是:

*((int*)(0))=1;

因为我对C/C++几乎没有经验,也没有尝试过很多,所以我不理解这个简单的表达式。它到底是什么意思?

5个回答

30

它的意图是让程序崩溃,在调试期间非常有用。

它将取消引用空指针并尝试向该内存分配一个值,这在理论上只是未定义行为,但在99%的系统中会导致访问冲突异常。

通常,它出现在以下情况下:

if ( !FileRead(importantFile) )
{
    // this should never happen, critical exception
    *((int*)(0))=1;
}

6
那么,它比 exit(EXIT_FAILURE); 更好在调试时(使用 gdb)会显示消息“程序以退出码 [EXIT_FAILURE] 退出”。 - 0xF1
1
@Don'tYouWorryChild 读取第一句话。退出将不会留下任何痕迹。 - Jim Balter
3
你真的不知道调试器的目的是什么吗?更好的目的是能够访问内存、变量和堆栈跟踪。 - Jim Balter
11
无需担心,无论是在调试器还是核心转储中,它都会生成一份堆栈回溯。但是,raise() 是更好的选择。 - Potatoswatter
11
以便于调试的方式退出有一个名称:abort()。关于原帖中的问题,将值分配给 NULL 指针是未定义行为;因此,编译器可以推断该行代码会导致未定义的行为,并将其完全优化掉。 - peppe
显示剩余9条评论

13

逐个分解。在左侧的外部括号中,您有:

(int*)(0)

这是将值0进行C语言样式的类型转换,转换为指向整型的指针,从而创建了一个空指针。

现在我们添加一个变量来接收第一个表达式的结果:

int* x = (int*)(0);

现在外部部分为:

*(x) = 1;

这里是对指针x进行解引用,然后将结果赋值为1。

由于(在这种情况下)x是一个空指针,这将导致解引用崩溃(严格来说,会在解引用后面的赋值时崩溃 - 请参见下面的评论)。通常用于强制崩溃或其他与系统相关的未定义行为; 通常用于测试或调试目的。在生产代码中不应该有像这样的代码。

注意:有些架构,通常是嵌入式系统中,其中零是有效的内存地址,并且上述代码可能具有合法的目的。但是,如果您正在这样的平台上工作,则不太可能在问题的语法方面遇到困难。


1
它不一定会在取消引用时崩溃。虽然取消引用空指针会产生未定义的行为,但通常在赋值期间的写入访问中发生崩溃。 - Arne Mertz
1
在左侧进行“解引用”实际上并不访问内存,并且无法与执行赋值的操作分离。 - Jim Balter
是的,在实现层面上,解引用指针只提供一个零作为实际读取或写入指令的参数。正是这个指令会失败。 - Andrew

9
正如其他人所指出的那样,这是一种进入调试器的方式。大多数计算机将拒绝写入内存地址0,并进入某种诊断模式。
然而,更好的选择是使用标准函数raise(),来自<signal.h><csignal>。即使在允许写入零地址的计算机(这样的机器确实存在!)和对未定义行为有宽松解释的编译器上,raise(SIGSEGV)也将产生与您的示例意图相同的结果。该操作不保证崩溃。从编译器的角度来看,它可能什么都不做,从编译后的程序中删除它也不会有错。
有关更详细的信号值,请参见man signal。可能意味着raise(SIGTRAP)abort()。在我的系统上,后者等同于raise(SIGABRT),但我懒得检查这种等价性是否由POSIX或C语言保证。

1
请参见https://dev59.com/0HVC5IYBdhLWcg3w0EsD - Suma
哪些机器允许写入地址零?这些主要是嵌入式设备吗?我认为这取决于操作系统(如果适用)以及它如何映射地址空间。 - Charles
1
@wrtmu 没错。任何带有虚拟内存的系统都可以将零设置为可写入状态。那些没有虚拟内存的系统(通常是嵌入式系统)可能会在那里放置中断向量。我最开始使用的是“经典”Mac OS,它最初是没有虚拟内存设计的。据我所知,前四个字节未使用;之后是中断处理程序。意外地往那里喷洒数据会使机器崩溃。 - Potatoswatter

2

这个*((int*)(0))=1;等同于

int *p=NULL;
*p=1;

2

这将在空指针处存储某些东西(并导致SEGFAULT)。 这对于停止程序并调用调试器以保留所有内存内容等非常有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接