分析一道很难的c语言题
前置知识(副作用和序列点部分)
源码一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #define _CRT_SECURE_NO_WARNINGS 1; #include <stdio.h> int fun(int i) { i = i < 3 ? i++ : 0; return i; } int main(void) { int i = 5, k, j; k = fun(i / 2); printf("%d,", k); k = fun(i = i / 2); printf("%d,", k); k = fun(i / 2); printf("%d\n", k); k = k < 3 ? k++ : 0;
return 0; }
|
输出结果
看到这样的输出结果很让人迷惑,难道是因为底层实现中,i++
的副作用先于赋值运算产生了吗?按照这个结论的话,执行代码i = i++;
之后,i
的值会保持不变,于是再去测试
测试源码二
1 2 3 4 5 6 7 8 9
| int main(void) { int i = 2;
i = i++; printf("%d", i);
return 0; }
|
测试结果
结果说明i++
的副作用并不一定会在赋值运算符=
之前发生,不过它到底在什么时候发生呢?我查看了C primer,在里面找到了答案,对于这次这个题来说,i++
的副作用可以先于赋值运算符之前发生,也可以在之后发生,两种情况都没有错,具体取决于编译器的实现,用书中的话来讲"对于这种情况更精确地说,结果是未定义的,这意味着c标准并未定义结果是什么。"
书中的解释
反汇编源码一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| i = i < 3 ? i++ : 0; 00721875 cmp dword ptr [i],3 00721879 jge __$EncStackInitStart+33h (072188Fh) ---------------------我想这里是赋值运算的开始部分,先对i进行了备份------------ 0072187B mov eax,dword ptr [i] 0072187E mov dword ptr [ebp-0C4h],eax --------------------------------------------------------------------- ---------------------从这里开始,我想就是i++的部分----------------- 00721884 mov ecx,dword ptr [i] 00721887 add ecx,1 0072188A mov dword ptr [i],ecx ---------------------到这里,i++的副作用也已经产生了----------------- 0072188D jmp __$EncStackInitStart+3Dh (0721899h) 0072188F mov dword ptr [ebp-0C4h],0 ----------------------把i的备份重新赋值给i,覆盖了i++的值------------ 00721899 mov edx,dword ptr [ebp-0C4h] 0072189F mov dword ptr [i],edx ----------------------------------------------------------------- return i; 007218A2 mov eax,dword ptr [i] } 007218A5 pop edi 007218A6 pop esi 007218A7 pop ebx 007218A8 add esp,0C4h 007218AE cmp ebp,esp 007218B0 call __RTC_CheckEsp (0721244h) 007218B5 mov esp,ebp 007218B7 pop ebp 007218B8 ret --- 无源文件 ----------------------------------------
|
反汇编源码二
1 2 3 4 5 6 7 8 9 10
| i = i++; --------------------------赋值运算符的副作用-------------- 00B8187C mov eax,dword ptr [i] 00B8187F mov dword ptr [i],eax -------------------------赋值运算符的副作用完毕------------- -------------------------可以看出源码二中i++的副作用后于赋值运算符了,跟源码一中正好相反---------- 00B81882 mov ecx,dword ptr [i] 00B81885 add ecx,1 00B81888 mov dword ptr [i],ecx ----------------------------------------------------------------------------------------
|
总结:这次遇见的题目的确很难,不过我想这个问题不太正统,因为在C Primer中,作者已经声明了:如果一个变量多次出现在一个表达式中,不要对该变量使用递增或递减运算符。