乐趣区

关于c:C进阶17和操作符分析

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

退出移动版