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 语言进阶课程》。
如有错漏之处,恳请斧正。