在函数参数中使用前缀和后缀运算符时的不同和奇怪的结果

问题描述

代码

#include <stdio.h>

int main()
{
    int i = 3;
    printf("%d %d %d %d %d\n",i = 7,--i,i = 18,i+5,i = 0);
    printf("%d %d %d %d %d\n",i--,i = 0);

    return 0;
}  

输出

7 7 7 5 7
7 18 7 5 7

为什么我会得到这个输出,谁能向我解释一下 printf 语句中的这些表达式是如何执行的?

我的意思是,编译器考虑的顺序是什么?

解决方法

这里的问题是i中所做的更改由于缺少序列点而调用了未定义的行为。

表现出未定义行为的程序对编译过程应该如何发生没有强加任何要求,编译器可以按照它认为合适的方式处理它,不同的编译器甚至同一编译器的不同版本会产生不同的结果。

虽然不是必需的,但他们通常做的一件事是产生一些警告,前提是启用了警告标志,有些甚至不需要,demo

相关标准条目:

N1570 ISO/IEC 9899:201x C11

§6.5 表达式:

2 - 如果对标量对象的副作用相对于对同一标量对象的不同副作用或使用同一标量对象的值进行的值计算而言是未排序的,则行为未定义。如果一个表达式的子表达式有多个允许的排序,并且在任何排序中出现这种未排序的副作用,则行为未定义。

需要明确的是,逗号运算符不是一个序列点。

我还应该注意到函数参数的计算顺序是未指定的。

§6.5.2.2 函数调用

10 - 在函数指示符和实际参数的计算之后但在实际调用之前有一个序列点。调用函数(包括其他函数调用)中的每一个在被调用函数体执行之前或之后没有特别排序的求值都相对于被调用函数的执行是不确定的。

正如已经指出的,这不是您应该向任何人展示的代码,因为在这种情况下是出于学习目的。

,

参数的求值顺序未指定,对同一对象的无序修改具有未定义的行为。

所以,正式地说,对代码进行推理是没有意义的。

然而,它是可以解释的,从正确的角度进行评估,而不是不合理或随机的。
(请注意,大多数参数与传递 i; i = 18 "is" i 相同,而不是 18。)

第一个:

i = 0;
int new_variable = i + 5;
i = 18;
i -= 1;
i = 7;
printf("%d %d %d %d %d\n",i,new_variable,i);

第二个:

i = 0;
int new_variable = i + 5;
i = 18;
int previous_i = i;
i = 7;
printf("%d %d %d %d %d\n",previous_i,i);
i -= 1;
,

这是一段很奇怪的代码,你永远不应该渴望写这样的代码,它会带来不必要的混乱。

可以根据编译器设置以任何顺序计算函数参数,因此此代码的输出可能因您的编译器和设置而异。

我假设你在某个学校的课堂上看到过这段代码,这种类型的例子通常会在教师试图解释后增量和前增量时出现。

请理解前增量和后增量的区别拳头:

  • ++x (pre-increment) 的意思是“增加变量; 表达式是最终值"
  • x++ (post-increment) 的意思是“记住原始值,然后 增加变量;表达式的值是原始值 价值"

编辑:更改解释以免引起混淆,输出可能因多个变量而异。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...