Summary
1)++和--参与混合运算结果是不确定的
,如r = (i++) + (i++);等
- C++只规定了++和--对应指令的
相对执行次序
(取值和自增的
相对顺序) - ++和--
对应的汇编指令不一定连续执行
- 在混合运算中,++和--的
汇编指令可能被打断执行
(取值和自增可能被打断了,中间插入了其他代码)
2)编译器的贪心法
- 编译器以
从左向右
的顺序,一个一个地尽可能多的读入字符
- 当读入的字符
不可能
和已经读入的字符组成合法符号为止
3)空格
可以作为C语言中一个完整符号的休止符
,编译器读入空格后会立即对之前读入的符号进行处理
。
++和--操作符剖析
1、Demo1
以下代码的输出是?
int i = 0; int r = 0; r = (i++) + (i++) + (i++); printf("i = %d\n, r = %d\n", i, r); r = (++i) + (++i) + (++i); printf("i = %d\n, r = %d\n", i, r);
代码初步分析:
第一行输出:i = 3, r = 3; // 按照从左向右的计算方法,i先取值,再自增, // 则r = 0 + 1 + 2 = 3; 第二行输出:i = 6, r = 16; // 同上,r = 4 + 5 + 6 = 15;
以上代码在VS2015编译器的实际输出结果为:
i = 3, r = 0;
i = 6, r = 18;
借助Vs编译器查看反汇编结果:
代码分析:
对于r = (i++) + (i++) + (i++); // 先取了i的值做了相加,赋值给r;然后i自增3次
在汇编层面,做的操作依次是:
1)00C542BC mov 将i代表的这个地址中的值放到寄存器eax中,为0
2)00C542BF add eax的值和i的值累加,累加和仍然为0
3)00C542C5 mov 将eax里的值放到r变量代表的内存里,
所以r的值为0
4)后面做了3次相同的操作:将i变量内存里的值放到ecx寄存器里,然后加1,
再把ecx里的值放回i变量的内存里;重复2次
所以i的值为3
对于r = (i++) + (i++) + (i++); // 先给i自增了3次;然后把i的值加3次给了r
在汇编层面,做的操作依次是:
1)009A42F8 到 009A42FE 将i代表的这个地址中的值放到寄存器eax中;
然后eax里的值自增1;
写回i的内存里,此时i的值为4
重复2次后,i的值为6
所以i的值为6
2)009A4313 mov 把i里的值移动到eax寄存器中,eax里的值为6
3)009A4316 add eax里的值加上i的值,即6+6,eax里的值为12
4)009A4319 add eax里的值加上i的值,即12+6,eax里的值为18
5)009A4319 mov eax里的值写回r代表的内存里
所以r的值为18
这一段代码,我们的分析和实际的编译器结果大不相同
;那么其他编译器又如何?bcc编译器和vc编译器的结果一致
,而gcc编译器的结果则是“r = 0 和 r = 16”
以上代码在java编译器中的输出结果:
// test.java
public static void main(String[] args) {
System.out.println("test.java file name must be equal to the class name test");
int i = 0;
int r = 0;
r = (i++) + (i++) + (i++);
System.out.println("i = " + i + ", r = " + r);
r = (++i) + (++i) + (++i);
System.out.println("i = " + i + ", r = " + r);
}
// 编译:javac test.java
// 执行:java test
// 输出:i = 3, r = 3
i = 6, r = 15 和分析的一致
以上的测试说明:++和--参与混合运算结果是不确定的
- C++只规定了++和--对应指令的相对执行次序
- ++和--对应的汇编指令不一定连续执行
- 在混合运算中,++和--的汇编指令可能被打断执行
2、Demo2
以下代码的输出是?
int i = 0; int j = ++i+++i+++i; int a = 1; int b = 4; int c = a+++b; int* p = &a; b = b/*p; printf("i = %d\n", i); printf("j = %d\n", j); printf("a = %d\n", a); printf("b = %d\n", b); printf("c = %d\n", c);
编译器的贪心法
- 编译器以
从左向右
的顺序,一个一个地尽可能多的读入字符
- 当读入的字符
不可能
和已经读入的字符组成合法符号为止
代码分析:
int j = ++i+++i+++i;
// 编译器先读到1个'+',不知道啥意思;
// 继续读到1个'+',它觉得这是个前置的'++'
// 然后读到了i,确定了这是个'++i'表达式
// 继续读到1个'+',这可能是一个加法的运算符 '+'
// 继续读到1个'+',这时候判断是一个后置的 '++',后面再读任何数都不对了,读到变量,不合法;读到符号,也不对;
// 这时候编译器就停止处理了,所以编译器就得到了 “++i++”
// 然后计算得到了 “1++”,对一个右值1进行自增,自然会编译错误!
int c = a+++b;
// 编译器依次读了3个字符'a++',知道这是个后置的++
// 然后读到了1个'+',觉得这个可能是个加法运算符
// 继续读到了b,后面也没有其他字符了,所以得到了 (a++) + b
// 计算得到了c = 5
b = b/*p; // error,编译器会将/*识别为注释,因此整个程序会编译失败
// 如果愿意是用b的值除以*p的值,那么就应该用括号或者空格表明
b = b / (*p); 或 b = b / *p;
本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。
如有错漏之处,恳请指正。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。