为什么这个goto的宏定义会导致程序崩溃?

3

我正在阅读一个帖子,其中有一个答案包含以下代码:

#define goto { int x = *(int *)0; } goto

作者在那里指出,每当一个人试图使用goto语句时,他的程序都会崩溃,那么问题来了:为什么?根据我的理解,int x = *(int *)0;将内存地址中前4字节的任何内容分配给x,但为什么这一定会使程序崩溃呢?


4
这位作者不应该编写程序,移除C语言编程中非常有用的“goto”关键字,仅因为他们不知道如何使用它。...?此外,声称它会导致程序崩溃是错误的,它将引起未定义的行为,因此程序的行为是不可预测的。这意味着它既无法防止任何问题,同时还会引起未知的麻烦。因此,如果我是你,我会寻找另一本书。 - Iharob Al Asimi
1
@iharob:OP所指的不是某本书,而是一篇被社区点赞的SO答案。有时候看到毫无意义的回答被点赞,我感到非常沮丧。 - Abhijit
1
@downvoters:请不要对这个问题进行负评,而是对与这个问题相关的被误导的发帖者OP所链接的得到赞同的SO答案进行点赞。 - Abhijit
1
如果你觉得必须惩罚使用goto,那么不可靠的运行时错误是一个愚蠢的方式。更好的方法是:#define goto @将强制在任何使用goto的地方产生语法错误。更好的方法是,在你的编码标准中禁止使用goto;如果你不能信任你的程序员遵循你的编码标准,那么你就有更大的问题了。 - Keith Thompson
3个回答

7

程序崩溃是因为您取消引用了一个NULL指针,但是程序会崩溃这一点是未定义的。它可能会崩溃,也可能不会崩溃或者做些奇怪的事情。这是一种未定义行为,最好避免使用。

值得注意的是,goto并非毫无用处。每个关键字都有其适用的场合,语言的作者和标准委员会成员仍有理由保留它(考虑到编程语言正在发生一些重大变革)。如果明智地使用,goto在C和C++中具有应有的位置。

仅仅为了让你明白这是未定义行为,我使用VC11在调试与发布模式下编译了上面的片段。在调试模式下,它崩溃了;而在发布模式下,编译器简单地优化掉了该语句,程序没有崩溃。


1
非常感谢!我想我明白了! - Allanqunzi
将代码更改为 #define goto { int x = *(int volatile*)0; } goto 以获得更一致的崩溃。 - chqrlie

0
在C或C++中,内存地址0表示一个空指针。对这样的指针进行解引用总是未定义的行为,在大多数实现中会导致分段错误,从而有效地"崩溃"程序。

1
0转换为指针类型是一个空指针。它不一定是内存地址0。对空指针进行解引用是未定义的行为,编译器可以生成在执行此操作时具有任意行为的代码。 - Pascal Cuoq
从C++运行时的角度来看,包含值0的指针确实指向地址0,我不是在谈虚拟地址,也许这很显然。 - rems4e
再次强调,当我说一个值为0的指针时,我指的是在语言中受到0整数文字影响的指针。从语言的角度来看,该值将始终为0,因此您可以稍后检查是否等于0。我从未谈论过位模式,我只谈论过与任何底层架构无关的地址0。获取位于地址X处的对象的地址会产生一个值为X的指针,因此我的类比是地址和指针值之间的对应关系。 - rems4e
你没有说“一个值为0的指针”。你说的是“内存地址0”,这在C或C++标准中没有任何意义(对象和函数有地址。地址不是整数。标准中没有“地址0”和“地址17”)。另一方面,在较低的层次上,“地址”是从整数到值的映射的键的名称,称为“内存”。空指针不必是此映射的键0。 - Pascal Cuoq
换句话说:{ void *p, *q; p = 0; memset(&q, 0, sizeof q); return memcmp(&p, &q, sizeof q); } 并不一定会返回 0。实际上我不知道有哪台计算机不会返回 0,但C标准就是这么说的。 - chqrlie
显示剩余3条评论

0

该代码有两个未定义行为。

首先,重新定义语言关键字本身就会产生未定义行为。

其次,正如其他人所说,值为`0'的指针是NULL指针,对其进行解引用将产生未定义行为。

因此,这种构造的实际结果是未定义的。未定义行为的常见症状是程序崩溃。然而,崩溃并不是真正保证的。甚至没有必要。

至于为什么编写宏,那段代码的作者显然是“goto是邪恶的”阵营的拥护者,并认为任意地强加他/她的无知偏见于他人是合适的。事实上,即使有更好的替代方法,goto也有其用途。即使Dijkstra(通常被引用来证明不使用goto)只描述了无节制使用的问题,也没有声称它应该被简单地避免。Donald Knuth在70年代中期撰写了一篇论文,其中包括使用 goto 有益的例子。


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