有趣的++i和i++

  作为一个天天和代码“约会”的人来说i++和++i这玩意再熟悉不过了,因为使用频率太高了。

虽然如此,但也未必见得我们真的了解她,不妨猜猜下面的输出结果。

 1 #inlcude <stdio.h>
 2 
 3 int main(void)
 4 {
 5     int i = 0, j = 0;
 6     
 7     printf("i[1] = %d i[2] = %d
", i++ + ++i, ++i + i++);
 8     
 9     printf("j[1] = %d j[2] = %d
", j++ + j++, ++j + ++j);
10     
11     return 0;
12  } 

  

  最后结果是:

  i[1] = 6 i[2] = 2

  j[1] = 4 j[2] = 4

  想要得出正确答案,仅仅知道前+和后+的区别是不够的,这里面有两个坑。

  第一个是cpu处理前+和后+真正的执行过程。

  第二个是printf这个“小妾”暗藏一腿。

  先把简单的第二个坑填了,以一般的思维和习惯会下意识的认为printf先计算前面那个表达式(即代码中的i[1] 和 j[1]),然后计算后面那个表达式的值(即  代码中的i[2]和j[2]),事实却正好相反,要先算后面再算前面。

  要想填平第一个大坑,我们需要研究编译后的汇编代码。

  

 1  @by tid_think
 2  @arm-linux-gcc -S test.c -o test.s
 3  @tiny4412
 4     .cpu arm1176jzf-s
 5     .eabi_attribute 27, 3
 6     .fpu vfp
 7     .eabi_attribute 20, 1
 8     .eabi_attribute 21, 1
 9     .eabi_attribute 23, 3
10     .eabi_attribute 24, 1
11     .eabi_attribute 25, 1
12     .eabi_attribute 26, 2
13     .eabi_attribute 30, 6
14     .eabi_attribute 18, 4
15     .file    "test.c"
16     .section    .rodata
17     .align    2
18 .LC0:
19     .ascii    "i[1] = %d i[2] = %d1200"
20     .align    2
21 .LC1:
22     .ascii    "j[1] = %d j[2] = %d1200"
23     .text
24     .align    2
25     .global    main
26     .type    main, %function
27 main:
28     @ args = 0, pretend = 0, frame = 8
29     @ frame_needed = 1, uses_anonymous_args = 0
30         
31                               @by tid_think
32                               @关键代码
33     stmfd    sp!, {fp, lr}    @将fp,lr两个寄存器的值压栈
34     add    fp, sp, #4         @fp = sp + 4
35     sub    sp, sp, #8         @sp = sp + 8
36     mov    r3, #0             @r3 = 0
37     str    r3, [fp, #-8]      @将r3的值(0)写入 fp -8 的地方 int i = 0
38     mov    r3, #0             @r3 = 0
39     str    r3, [fp, #-12]     @将r3的值(0)写入 fp -12 的地方 int j = 0
40     ldr    r1, .L2            @将.L2的地址加载到r1
41     ldr    r3, [fp, #-8]      @将fp -8地址上的值(i = 0) 给r3
42     add    r3, r3, #1         @r3 = r3 + 1   ===>i = 1
43     str    r3, [fp, #-8]      @将r3的值(1)写入 fp -8 的地方 i
44     ldr    r2, [fp, #-8]      @将fp -8地址上的值(i = 1) 给r2
45     ldr    r3, [fp, #-8]      @将fp -8地址上的值(i = 1) 给r3
46     add    r2, r2, r3         @r2 = r2 + r 3 = 2 
47     ldr    r3, [fp, #-8]      @将fp -8地址上的值(i = 1) 给r3
48     add    r3, r3, #1         @ r3 = r3 + 1 = 2
49     str    r3, [fp, #-8]      @将r3的值(2)写入 fp -8 的地方 i
50     ldr    r3, [fp, #-8]      @将fp -8地址上的值(i = 2) 给r3
51     add    r3, r3, #1         @ r3 = r3 + 1 = 3
52     str    r3, [fp, #-8]      @将r3的值(3)写入 fp -8 的地方 i
53     ldr    r0, [fp, #-8]      @将fp -8地址上的值(i = 3) 给r0
54     ldr    r3, [fp, #-8]      @将fp -8地址上的值(i = 3) 给r3
55     add    r3, r0, r3         @ r3 = r0 + r3 = 6
56     ldr    r0, [fp, #-8]      @将fp -8地址上的值(i = 3) 给r0
57     add    r0, r0, #1         @r0 = r0 + 1 = 4
58     str    r0, [fp, #-8]      @将r0的值(4)写入 fp -8 的地方
59     mov    r0, r1             @r0 = r1    给printf传参数1 
60     mov    r1, r2             @r1 = r2 = 2   给printf传参数2 
61     mov    r2, r3             @r2 = r3 = 6   给printf传参数3
62     bl    printf              @调用printf打印
63     ldr    r1, .L2+4          @开始计算j了............
64     ldr    r2, [fp, #-12]
65     ldr    r3, [fp, #-12]
66     add    r2, r2, r3
67     ldr    r3, [fp, #-12]
68     add    r3, r3, #1
69     str    r3, [fp, #-12]
70     ldr    r3, [fp, #-12]
71     add    r3, r3, #1
72     str    r3, [fp, #-12]
73     ldr    r3, [fp, #-12]
74     add    r3, r3, #1
75     str    r3, [fp, #-12]
76     ldr    r3, [fp, #-12]
77     add    r3, r3, #1
78     str    r3, [fp, #-12]
79     ldr    r0, [fp, #-12]
80     ldr    r3, [fp, #-12]
81     add    r3, r0, r3
82     mov    r0, r1
83     mov    r1, r2
84     mov    r2, r3
85     bl    printf
86     mov    r3, #0
87     mov    r0, r3
88     sub    sp, fp, #4
89     ldmfd    sp!, {fp, pc}
90 .L3:
91     .align    2
92 .L2:
93     .word    .LC0
94     .word    .LC1
95     .size    main, .-main
96     .ident    "GCC: (ctng-1.8.1-FA) 4.5.1"
97     .section    .note.GNU-stack,"",%progbits    

汇编代码解析:

  首先刨去没用的信息,直接从 31行开始看

  33~35行都是对栈指针的一些偏移和保存。

  从以上汇编代码看可以看出简单的两句C编译成汇编就是一大坨,如果用纯汇编写15行左右应该就能搞定 执行效率几乎是C的20倍……………………

深圳宝安华美居
原文地址:https://www.cnblogs.com/tid-think/p/5136574.html