Why this macro define of goto will crash the program?
我在读一篇文章,在其中一个答案中有以下代码
1
| #define goto { int x = *(int *)0; } goto |
在这里作者指出,每当一个人试图使用goto语句时,他的程序就会崩溃,我的问题是为什么?根据我的理解,int x = *(int *)0;将内存地址前4个字节中的任何内容分配给x,但为什么这肯定会使程序崩溃?
- 作者不应该编写程序,仅仅因为他们不知道如何使用它,就删除了C程序的一个基本部分,比如非常有用的goto。…?而且,说它会使程序崩溃是错误的,它会调用未定义的行为,因此程序的行为是不可预测的。这意味着当它引起未知的问题时,它什么也阻止不了。所以,如果我是你,我会找一本不同的书。
- @伊哈罗布:这不是某本书所指的,而是一个被否决的答案。有时,我看到社区对毫无意义的答案投了反对票,感到很沮丧。
- @投反对票的人:请不要投反对票,而应该投反对票的人,因为他们的回答与这个问题有关,而这个问题是误导性的海报,如OP。
- 如果您觉得必须惩罚使用goto,那么不可靠的运行时错误是一种愚蠢的方法。更好:#define goto @将强制使用goto的任何语法错误。更好的是,你可以在你的编码标准中禁止使用goto;如果你不能信任你的程序员遵循编码标准,你会遇到更大的问题。
只是因为您正在取消对NULL指针的引用。但是程序崩溃的定义还没有确定。它可能会崩溃,可能不会崩溃或者做一些奇怪的事情。这只是一种不明确的行为,最好避免。
值得注意的是,goto并没有那么无用。每一个关键词都有自己的位置,语言作者和标准委员会的成员有理由继续保持它(考虑到语言正在发生一些巨大的变化)。如果明智地使用,Goto在C和C++中有一个适当的位置。
Just to give an idea how this is an UB, using VC11, I compiled the
above snippet in debug and release mode. In debug mode it crashed but
in release mode, compiler simply optimized out the statement and there
was no crash.
号
- 非常感谢!我想我明白了!
- 将代码更改为#define goto { int x = *(int volatile*)0; } goto,以获得更一致的崩溃。
该代码在两个方面给出了未定义的行为。
首先,重新定义语言关键字本身会产生未定义的行为。
第二,正如其他人所说,值为"0"的指针是空指针,取消对它的引用会给出未定义的行为。
因此,构造的实际结果是未定义的行为。未定义行为的一个常见症状是程序崩溃。然而,实际上并不能保证撞车。它甚至不是必需的。
作为"为什么?"在编写宏的过程中,该代码的作者显然是"Goto is Evil"阵营的订阅者,他/她认为自己适合任意地对他人施加他/她的未成形的偏见。实际上,即使替代方案通常更可取,goto也有其用途。即使是Dijkstra(原稿的作者,人们经常引用它来证明不使用goto)也只描述了自由使用的问题,并没有声称应该简单地避免使用。DonaldKnuth后来在70年代中期写了一篇关于这个主题的论文,包括使用goto是有益的例子。
在C或C++中,内存地址0表示空指针。取消对此类指针的引用总是未定义的行为,在大多数实现中,它会引发分段错误,从而有效地"破坏"程序。
- 到0指针类型的转换是一个空指针。它不一定是内存地址0。取消对空指针的引用是未定义的行为,编译器可以在执行过程中生成具有任意行为的代码。
- 从C++运行时的角度来看,包含值0的指针确实指向地址0,不是吗?我不是在说虚拟地址,也许它很清楚。
- 否,值0的指针转换不必是地址0。在c中,这在c faq:c-faq.com/null/index.html的第5节中介绍(请参见具体的5.3和5.5)。C++在这方面没有任何额外的规则,它会迫使空指针的位模式表示0。
- 同样,当我说一个值为0的指针时,我的意思是一个指针,它影响了语言中的0整型文字。从语言的角度来看,该值始终为0,因为您可以稍后根据0检查相等性。我从来没有讨论过位模式,我只讨论了地址0,任何底层架构的不可知论者。取位于地址x的对象的地址,得到一个值为x的指针,因此我的地址和指针值之间是平行的。
- @Rems4e:看起来你把真实地址和虚拟地址混淆了
- @你为什么这么想?我注意到在我的讨论中没有提到真实的地址,因为它们与问答和评论完全无关。
- 您没有说"值为0的指针"。你说"内存地址0",在C或C++标准中没有任何意义(对象和函数有地址)。地址不是整数。标准中没有"地址0"和"地址17")。另一方面,在较低的层次上,"地址"是从整数到称为"内存"的值的映射键的名称。空指针不必是此映射的键0。
- 换句话说:{ void *p, *q; p = 0; memset(&q, 0, sizeof q); return memcmp(&p, &q, sizeof q); }不一定返回0。我不知道任何物理计算机在哪里不可能,但C标准是这样说的。