是printf的输出("%d%d",c ++,c); 也未定义?

Sne*_*ish 20 c undefined-behavior

我最近发现了一篇帖子cout << c ++ << c ;?的正确答案是什么?并且想知道是否输出

int c = 0;  
printf ("%d %d", c++, c);  
Run Code Online (Sandbox Code Playgroud)

还未定义?

我已经在讲座中研究了修复后和前缀运算符只有在得到分号后才增加值.所以据我说,输出0 0是正确的!

Joh*_*ode 19

我已经在讲座中研究了修复后和前缀运算符只有在得到分号后才增加值.

把你的讲师送给我,这样我就可以拿棒球棒礼貌地指出他的错误.

恰好当前或后缀的副作用++--施加是未指定的,除了要求其下一个序列点之前发生.在表达式中

x = a++ * b
Run Code Online (Sandbox Code Playgroud)

a可以a++在评估之后立即更新,或者可以推迟更新直到a++ * b评估并将结果分配给x或之间的任何地方.

这就是为什么表达喜欢i++ * i++printf("%d %d", c++, c)a[i++] = i其他人都是糟糕的juju.您将根据编译器,优化设置,周围代码等获得不同的结果.语言标准明确地将行为保留为未定义,以便编译器没有义务"做正确的事情",无论正确的事情是什么.请记住,未定义行为的定义是

3.4.3

1 使用不可移植或错误的程序结构或错误数据时的未定义行为
行为,本国际标准不对其施加任何要求

2注意可能未定义的行为包括完全忽略不可预测的结果,在转换或程序执行期间的行为记录的环境特征(有或没有发出诊断消息),终止翻译或执行(发布诊断消息).

3示例未定义行为的示例是整数流上的行为.

这是一个深思熟虑的设计决策 - 未指定这些操作的顺序的基本原理是为了实现自由,重新安排评估顺序以进行优化.但是,为了换取这种自由,某些操作不会有明确的结果.

请注意,编译器可以自由尝试检测这些情况并发出诊断信息; printf("%d %d", c++, c);很容易捕获,但这将是一般情况下检测的bugger.想象一下,如果这是写的printf("%d %d", (*p)++, c); 如果p指向c,那么行为是未定义的,否则它没关系.如果p在不同的翻译单元中分配,那么在编译时无法知道这是否是一个问题.

这个概念并不难理解,但它是最一贯误解的一个(误)的C语言方面.毫无疑问,这就是Java和C#语言规范强制对所有内容执行特定评估顺序的原因(所有操作数从左到右进行评估,并立即应用所有副作用).

  • 很好的答案 - 除了你没有引用标准的部分,说明它是未定义的:"在前一个和下一个序列点之间,一个对象的表达式应该通过表达式的评估最多修改一次.此外,先前的值应该是只读的,以确定要存储的值." (4认同)

Ed *_* S. 6

我已经在讲座中研究了修复后和前缀运算符只有在得到分号后才增加值

这不是标准描述它的方式.甲序列点是在代码点,其中,其可能发生在代码之前的部分的副作用已被评估.函数参数之间的逗号不是序列点,因此其行为未定义.

函数参数的评估顺序未指定.无法保证函数的参数将按顺序进行求值(1, 2, N),因此无法保证在传递第二个参数之前将对增量进行求值.

所以据我说,输出0 0是正确的!

不,行为未定义,因此您无法合理地声称输出将为0 0.

  • @snehasish:你没有仔细阅读.这不是逗号运算符(它是一个序列点)的示例,并且未指定函数参数中的求值顺序. (3认同)