有趣的++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\n", i++ + ++i, ++i + i++); 8 9 printf("j[1] = %d j[2] = %d\n", 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] = %d\012\000" 20 .align 2 21 .LC1: 22 .ascii "j[1] = %d j[2] = %d\012\000" 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倍……………………
深圳宝安华美居