在贴吧看到有吧友问Java和C对于同一代码的运行得到不同的结果,代码如下:
// 代码1
int a;
a= ++a + a++;
printf("a=%d",a);
输出为:a=3
我在DevC++5.7.1上测试了这段代码,并尝试了一个修改:
// 代码2
int a;
a= a++ + ++a;
printf("a=%d",a);
输出为:a=2
竟然出现了不同!
正在学习C语言,所以调试了一下,保留后缀形式,尝试以下代码:
// 代码3
int a;
a= ++a + a;
printf("a=%d",a);
输出为:a=2
这个很好理解,因为在“=”“++”“+”三个运算符中,优先级顺序是“++”“+”“=”;并且对于++a,它等价于以下代码:
// 代码4
int a=0;
a=a+1;
return a;
因此代码3中的赋值语句,应该是a=1+1
。
我又尝试了保留前缀形式,即以下代码:
// 代码5
int a;
a= a + a++;
printf("a=%d",a);
输出为:a=1
运算上同样有优先级顺序:先计算a++,它等价于以下代码:
// 代码6
int a=0;
return a;
a=a+1;
显然a++=0
,然后计算a + a++
,因为经过自增之后a=1
,因此代码5中的赋值语句,应该是a=1+0
。
和前面差不多,但是得到了一些新东西。我原本将后缀的值的使用理解为“该语句执行完毕才更新值”,现在看来,在同一行内就已经更新了值,并且传递给了变量a
。
回过头去看代码2,很清楚,根据优先级顺序计算即可:
首先a++=0
,但是a=1
,在此基础上++a=2
,因此代码2的赋值语句应该为:a=0+2
再回头看代码1,这就有问题了,因为按道理说,他的结果应该和代码2一样,但是不一样,我想要搞明白哪里的问题,可我理论知识不足,因此我需要试探一下:
试探什么地方?我怀疑a++
自增之后,在求和时,使用了更新后的值,而不是原始值0
试探方法很简单,因为a++
的优先级最高,可以仅保留a++
自增的效果(也就是在声明时int a=1;
),不使用更新后的值(也就是把a++
替换为0
)。
// 代码7
int a=1;
a= ++a + 0;
printf("a=%d",a);
输出为:a=2
这是显而易见的,但这也是符合预期的,只不过把a++
的动作进行了手动设置。
再试探一下,将0
改为a
,看看是否符合猜想:
// 代码8
int a=1;
a= ++a + 1;
printf("a=%d",a);
输出为:a=3
符合猜想。那么尝试性地做一个检验:
// 代码9
int a=-1;
a= --a + (-1);
printf("a=%d",a);
输出为:a=-3
运行下面的代码,的确输出了-3
// 代码9
int a=0;
a= --a + a--;
printf("a=%d",a);
输出为:a=-3
说明存在这样的问题
我于是把a++``++a``a--``--a
进行组合,看看是否存在类似的异常,得到以下结果:
// a= a++ + ++a; // 0+2=2,前者先算,后者后算,前者用旧值,后者用新值 --正常
// a= ++a + a++; // 2+1=3,后者先算,前者后算,全用新值 --异常
// a= a-- + --a; // 0+(-2)=-2,前者先算,后者后算,前者用旧值,后者用新值 --正常
// a= --a + a--; // -2+(-1)=-3,后者先算,前者后算,全用新值 --异常
// a= --a + a++; // -1+0=-1,前者先算,后者后算 ,全用新值 --异常
// a= a++ + --a; // 0+0=0,前者先算,后者后算,前者用旧值,后者用新值 --正常
// a= a-- + ++a; // 0+0=0,前者先算,后者后算,前者用旧值,后者用新值 --正常
// a= ++a + a--; // 1+0=1,前者先算,后者后算,全用新值 --异常
我觉得手算可以用表格,比较方便,以a= ++a + a++;
为例:
旧值 | 新值 | ||
---|---|---|---|
高优先级 | a++ | 0 | 1 |
低优先级 | ++a | 1 | 2 |
运算顺序给定以后,只有4种可能的结果,因此很容易根据结果查看原因,结果输出是3,可以看出是a++
取1
导致结果与预想的不同。
最后,按照书上的说法,采取这种写法可以加快运行速度,这才是使用它的原因,而不是因为写起来简单。因此,不想导致未知的错误,不想一点一点的排查,最好的办法就是将代码写得清晰易懂,而不是为了追求简洁把代码写得复杂无比,不止为了别人看得懂,更重要是为了自己检查时方便。
这种++--写得很复杂还是要看的,自己虽然不去写,但是还是有不少面试题会考,该会还是要会
至于这些异常出现的原因,我认为是编译器的锅,我没有能力解决问题,我只是把我可能遇到的问题进行了总结。