use*_*279 35 c undefined-behavior
我们班的C编程教授问了这个问题:
你得到的代码:
int x=1;
printf("%d",++x,x+1);
Run Code Online (Sandbox Code Playgroud)
它总会产生什么输出?
大多数学生说未定义的行为.任何人都可以帮助我理解为什么会这样吗?
感谢编辑和答案,但我仍然感到困惑.
Jer*_*fin 40
在每种合理的情况下,输出可能是2.实际上,你所拥有的是未定义的行为.
具体来说,标准说:
在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算来修改一次.此外,先前的值应该只读以确定要存储的值.
在评估函数的参数之前有一个序列点,并且在评估了所有参数之后的序列点(但是尚未调用的函数).在这两者之间(即,在评估参数时),没有序列点(除非参数是表达式在内部包含一个,例如使用&&
||
或,
运算符).
这意味着该呼叫到printf
被读取的先前值二者来确定所存储的值(即,++x
)和,以确定第二个参数的值(即,x+1
).这显然违反了上面引用的要求,导致了不确定的行为.
事实上,您提供了一个额外的参数,没有给出转换说明符,这不会导致未定义的行为.如果您提供的转换说明符的参数较少,或者参数的(提升的)类型与转换说明符的参数不一致,则会得到未定义的行为 - 但是传递额外的参数则不会.
Gil*_*il' 14
任何时候程序的行为都是不确定的,任何事情都可能发生 - 经典的短语是"恶魔可能会飞出你的鼻子" - 尽管大多数实现都没有那么远.
函数的参数在概念上是并行评估的(技术术语是它们的评估之间没有序列点).这意味着表达++x
和x+1
可能的顺序进行评价,以相反的顺序,或以某种方式交错.修改变量并尝试并行访问其值时,行为未定义.
对于许多实现,参数是按顺序计算的(尽管并不总是从左到右).所以你不太可能在现实世界中看到任何东西.
但是,编译器可以生成如下代码:
r1
. x+1
通过添加1来计算r1
.++x
通过添加1来计算r1
.那没关系,因为x
已加载r1
.鉴于编译器的设计方式,步骤2不能进行修改r1
,因为只有x
在两个序列点之间读取和写入时才会发生这种情况.这是C标准禁止的.r1
成x
.在这个(假设但正确的)编译器上,程序将打印3.
(编辑:传递一个额外的参数printf
是正确的(N1256中的§7.19.6.1-2 ;感谢Prasoon Saurav)指出这一点.另外:添加一个例子.)
AnT*_*AnT 11
正确答案是:代码产生未定义的行为.
该行为是未定义的原因是,两个表达式++x
和x + 1
正在修改x
和读取x
用于不相关的(变形例)的原因,并且这两个动作没有被序列点分离.这导致C(和C++)中的未定义行为.要求以6.5/2的C语言标准给出.
请注意,在这种情况下,未定义的行为与printf
函数只给出一个格式说明符和两个实际参数这一事实完全无关 .为了给出更多的参数,printf
格式字符串中的格式说明符在C语言中是完全合法的.同样,问题源于违反C语言的表达式评估要求.
还要注意,本次讨论的一些参与者未能理解未定义行为的概念,并坚持将其与未指定行为的概念混合在一起.为了更好地说明差异,让我们考虑以下简单示例
int inc_x(int *x) { return ++*x; }
int x_plus_1(int x) { return x + 1; }
int x = 1;
printf("%d", inc_x(&x), x_plus_1(x));
Run Code Online (Sandbox Code Playgroud)
上面的代码与原始代码"等效",除了涉及我们的操作x
被包装到函数中.在这个最新的例子中会发生什么?
此代码中没有未定义的行为.但由于printf
参数的评估顺序未指定,因此该代码会产生未指定的行为,即可能将printf
其称为printf("%d", 2, 2)
或作为printf("%d", 2, 3)
.在这两种情况下,输出确实是2
.但是,此变体的重要区别在于,所有访问x
都包含在每个函数的开头和结尾处出现的序列点中,因此此变体不会产生未定义的行为.
这正是其他一些海报试图强迫原始例子的原因.但它无法完成.原始示例产生未定义的行为,这是一个完全不同的野兽.他们显然试图坚持在实践中未定义的行为总是等同于未指明的行为.这是一个完全虚假的主张,只表明制造它的人缺乏专业知识.原始代码产生未定义的行为,句点.
要继续该示例,我们将以前的代码示例修改为
printf("%d %d", inc_x(&x), x_plus_1(x));
Run Code Online (Sandbox Code Playgroud)
代码的输出通常是不可预测的.它可以打印2 2
或打印2 3
.但请注意,即使行为不可预测,它仍然不会产生未定义的行为.行为未指定,位未定义.未指定的行为仅限于两种可能性:2 2
或者2 3
.未定义的行为不限于任何内容.它可以格式化硬盘而不是打印东西.感到不同.
大多数学生表示行为未定义。谁能帮助我理解为什么会这样?
因为函数参数的计算顺序没有指定。
归档时间: |
|
查看次数: |
1559 次 |
最近记录: |