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.javapublic 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 = 5b = b/*p;    // error,编译器会将/*辨认为正文,因而整个程序会编译失败// 如果违心是用b的值除以*p的值,那么就应该用括号或者空格表明b = b / (*p); 或 b = b / *p; 

本文总结自“狄泰软件学院”唐佐林老师《C语言进阶课程》。
如有错漏之处,恳请斧正