Twisted C++ code
Possible Duplicate:
Undefined behavior and sequence points
1 2 3 4 5 6 7 8
| #include< iostream.h>
int main()
{
int i=7,j=i;
j=(i++,++i,j++*i);
cout <<j;
return 0;
} |
C++代码的输出将是什么?
教授给我的作业。
- 什么都不会发生,因为没有任何std::out操作。
- 技巧问题-尽管包含iostream.h,但没有输出。你大概是说i和j的最终值是多少?
- 你的教授为什么还在做#include ?你们在使用Turbo C++吗?P
- 对不起,刚开始没写过cout。现在加上了。
- 此外,IoSurviv.H已经"预先标准化"了10年(PARASIFIT.COM/C++FAQ LIT/编码标准。
- 这必须来自"代码混淆101"类
- 这是什么可怕的教授?
- 出色的家庭作业
- 在我的机器上,输出是"你妈"。
- 史蒂夫M怎么能印你妈的。j是整数而不是字符串。
- 然而,更多的证据表明,大学无法培养出真正的程序员。
- 请仔细检查您是否正确地重写了代码。如果是这样,那么仔细检查你的教授是否真的有资格教C++。我希望这是个错误。
- 在现实世界中,正确的答案是,该代码的输出是一个愤怒的程序员,他会追查作者并给他们一个耳光!
- @纳普斯特-@steve m忘记在他的评论上加上"幽默"标签。这里所要指出的是,在现实世界中,这不是一个特别有用的C++作业,因为编写这种代码的任何程序员都会在短时间内脱离工作。这里有一些答案可以帮助你理解它,作为一个学术练习。见@kos的回复。
- 我认为SteveM指的是程序的未定义行为,这允许他所说的输出发生。但是,很遗憾解释一下…:(
- 这些问题一周问两次。有几十个骗局。幸运的是,我们现在有了一个FAQ条目:未定义的行为和序列点。仔细阅读。我敢打赌它的弹药比你的教授能拿的还多。
有时,用几个编译器实际编译程序并观察结果有助于说服那些不相信这是未定义的人:
修正了iostream.h错误后,
- G++4.5.2打印64
- Clang++2.8打印63
- Sun C++ 5.8打印63
- MSVC 2010打印64
(噢,而且,重新编写以使用C I/O,Unix7上的原始K&R C编译器打印63)
[编辑以解释OP的问题更改编辑]:
输出是什么还没有定义。
- 型为什么你们都说什么不明确?
- 型@Napster:因为您提供给它的代码示例有一个未定义的输出。
- 型@john在j++和修改j的赋值表达式之间没有序列点。因此,你会得到未定义的行为。您需要将其重写为j=(i++,++i,j++,(j-1)*i);以定义此案例。"序列点"FAQ条目需要更多这样的例子,imho,并且可以在数学方面有所保留,比如定义"部分顺序"等等:)
- 型Johannes:我认为这将在C++0x中定义,但是在当前标准下,我不认为它是这样的;在EDCOX1与9的增量的分配之间仍然没有序列点,j的增量是相对于EDCOX1(18)的计算而排序的,而不是相对于EDCOX1〔9〕(和当前STAN的分配)的顺序。DARD不要求在计算值之后赋值)。
- 型@杰瑞,我同意这是含糊不清的,我当然不会在任何事情上下注。但有一些逻辑:在a = (x, y)中,在分配之前需要对(x, y)进行评估。评估意味着评估y,在x和y之间有一个序列点,这意味着评估y之前的所有副作用都是完整的。因此,在相同的两个序列点之间没有两个副作用:分配副作用发生在x和y之间的序列点之后。
- 型@litb,@jerry:correct,我看得不够广泛——只看逗号操作符。这与j = j++未定义的原因相同。
- 型我删除了我的评论以减少混乱
- 型Johannes:这是以前被争论过的:群组。谷歌。com /组/ Alt Cop.Lang.Cube。C-C++/BLUSEXY-FRM和ZWNJ;和8203,和/和Helip;通过C++ 03,我没有看到任何真正的理由来相信我当时的不同。
- 型@Jerry (a=b)=c在我看来是不明确的:)这是我的理由:因为在相同的两个序列点之间,a有两个修改。当然,a = b中的赋值必须在产生左值结果之前完成(赋值操作的结果是赋值发生后存储在左操作数中的值)。:因此我们可以说"一个赋值先于另一个赋值",但这并不重要:我们还有两个赋值在相同的两个序列点之间的a,所以行为仍然是ub(听起来很荒谬)。
- 型……但这不适用于a = (0, a++, 0):这里,两个分配将适当地分布在两个序列点上。
- @约翰内斯:对不起。当线程开始讨论(a=b)=c时,它也会涉及到很多其他方面,包括一些与当前问题更相关的方面——在使用结果值之前需要对表达式进行评估吗?
- 要求"赋值操作的结果是在赋值发生后存储在左操作数中的值"有另一个重要目的:它使a = b = 0定义的行为-规则是,b的前一个值只应用于确定要存储在b中的值。如果说左值是指任务完成后的b,那么B值的获取将获取B的新值。
- 我认为只要一个实现坚持下去,它就可以做任何事情。在抽象机中,存储在对象中的值仅由计算产生,因此如果实现以不同的方式计算值,则必须注意不要产生其他可见的结果。
代码中存在以下错误:
#include 应为#include ,
j未初始化,因此j++*i的值未知-好,这在编辑中得到修复,
另外,任务本身是不恰当的。卷积线可重写为:
1 2 3
| i++;
++i;
j = j++ * i; |
最后一部分因以下原因无效:
未定义的行为和序列点
- 型我初始化了J。
- 型(意外的)荣誉?用这句话获得双重意义:)
- 型i = 7, j = ij==7
- 型您所提供的工作分配的重写将为不保证的操作分配一个顺序。您可能希望在引用链接中发现这一点,但应该明确指出。
- 型@Gregg,我相信您把函数调用误认为是逗号运算符-逗号有一个定义良好的执行顺序,并引入了一个序列点(参见参考链接)。
- 型我仍然不明白为什么j = j++是未定义的,j = ++j是定义良好的。注意:-/
- 型@约翰·迪布林——如果我没弄错的话,那么在i++和++i之间有一个序列点,在++i和j++ * i之间有一个序列点,但在j++ * i和j=expression之间没有序列点,这就引入了ub——我们有2个独立的写入j中。哎呀,你让我困惑了一会儿。:)
- 型@先增加j,然后将结果值存储到j。在j = ++j中,您有两个"同时"的字。
- 型@科斯:是的,请看我在其他帖子中的评论。
- 型为什么我这里正好有-2…?:)
- 型@科斯,要点。
本质上,你是把i乘以2,乘以j的初始值,再加上1。
最后,j=64
1
| j = ((7+2)*7) + 1 = (9*7)+1 = 63+1 = 64 |
至少这是我的Visual Studio 2010编译器所做的。
- @Flevine100:是什么让你认为这样的事情在嵌入式社区中是合理的?
- 可能不是,但我看到的一些嵌入代码中充满了所谓的"节省空间"优化。就我个人而言,除非有一个不能被忽视的正当理由,否则我绝不会写这段代码。
- 只是一些不信任编者的例子,我强调。
- -1因为64是错误的,所以程序调用未定义的行为。
- 公平地说,我指定我的VS2010编译器生成了它,但在查看了其他编译器的结果后,我现在同意64只在VS2010和G++中是正确的。