往往这个问题被链接为与代码相关的重复问题,比如:
printf("%d %d\n", i, i++);
or
printf("%d %d\n", ++i, i++);
或其它类似变体。
虽然已经声明了这也是未定义行为,但当涉及到
printf()
时,与类似于以下语句的比较存在细微差别:
未定义行为。
x = i++ + i++
在以下声明中:
printf("%d %d\n", ++i, i++);
printf()
函数中的参数求值顺序是未指定的。这意味着,表达式i++
和++i
的求值顺序可以是任意的。C11 standard对此有一些相关描述:
附录J,未指定行为
在函数调用中,函数设计器、参数和参数中的子表达式的求值顺序(6.5.2.2)。
3.4.4,未指定行为
使用未指定的值或其他行为,在这种情况下,国际标准提供了两个或更多可能性,并且不对选择哪一个施加进一步要求。
例如,函数参数的求值顺序就是未指定行为。
未指定的行为本身并不是一个问题。考虑以下例子:
printf("%d %d\n", ++x, y++);
这也有未指定的行为,因为++x和y++的评估顺序是未指定的。但这是完全合法和有效的语句。在这个语句中没有未定义的行为,因为修改(++x和y++)是针对不同的对象完成的。
以下语句的呈现方式是什么?
printf("%d %d\n", ++i, i++);
作为“未定义行为”,这两个表达式修改了
i
对象的
相同部分,没有中间的
序列点。
另一个细节是printf()调用中的逗号是一个分隔符,而不是
逗号运算符。
这是一个重要的区别,因为逗号运算符确实在其操作数的评估之间引入了一个序列点,这使得以下操作合法:
int i = 5;
int j;
j = (++i, i++);
printf("i=%d j=%d\n",i, j);
逗号运算符从左到右评估其操作数,并仅产生最后一个操作数的值。因此,在
j = (++i, i++);
中,
++i
将
i
增加到
6
,而
i++
产生
i
的旧值 (
6
),该值被赋给
j
。然后,由于后增量,
i
变为
7
。
因此,如果函数调用中的 逗号 是逗号运算符,则
printf("%d %d\n", ++i, i++);
这不会成为问题。但是,因为这里的逗号是一个分隔符,所以它会引发未定义行为。
对于那些不熟悉“未定义行为”的人来说,阅读
《每个C程序员应该知道的关于未定义行为的事情》可以帮助理解C语言中的概念和许多其他变体的未定义行为。
此帖子:
未定义、未指定和实现定义行为也相关。
(i++)
的值仍为1。 - Drew McGoweni = (i++);
的目的是什么,肯定有更清晰的写法。即使它被明确定义了,这也是正确的。即使在 Java 中定义了i = (i++);
的行为,这仍然是糟糕的代码。只需编写i++;
即可。 - Keith Thompson