作为一个天天和代码“约会”的人来说i++和++i这玩意再熟悉不过了,因为使用频率太高了。
虽然如此,但也未必见得我们真的了解她,不妨猜猜下面的输出结果。
1 #inlcude2 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, 310 .eabi_attribute 24, 111 .eabi_attribute 25, 112 .eabi_attribute 26, 213 .eabi_attribute 30, 614 .eabi_attribute 18, 415 .file "test.c"16 .section .rodata17 .align 218 .LC0:19 .ascii "i[1] = %d i[2] = %d\012\000"20 .align 221 .LC1:22 .ascii "j[1] = %d j[2] = %d\012\000"23 .text24 .align 225 .global main26 .type main, %function27 main:28 @ args = 0, pretend = 0, frame = 829 @ frame_needed = 1, uses_anonymous_args = 030 31 @by tid_think32 @关键代码33 stmfd sp!, {fp, lr} @将fp,lr两个寄存器的值压栈34 add fp, sp, #4 @fp = sp + 435 sub sp, sp, #8 @sp = sp + 836 mov r3, #0 @r3 = 037 str r3, [fp, #-8] @将r3的值(0)写入 fp -8 的地方 int i = 038 mov r3, #0 @r3 = 039 str r3, [fp, #-12] @将r3的值(0)写入 fp -12 的地方 int j = 040 ldr r1, .L2 @将.L2的地址加载到r141 ldr r3, [fp, #-8] @将fp -8地址上的值(i = 0) 给r342 add r3, r3, #1 @r3 = r3 + 1 ===>i = 143 str r3, [fp, #-8] @将r3的值(1)写入 fp -8 的地方 i44 ldr r2, [fp, #-8] @将fp -8地址上的值(i = 1) 给r245 ldr r3, [fp, #-8] @将fp -8地址上的值(i = 1) 给r346 add r2, r2, r3 @r2 = r2 + r 3 = 2 47 ldr r3, [fp, #-8] @将fp -8地址上的值(i = 1) 给r348 add r3, r3, #1 @ r3 = r3 + 1 = 249 str r3, [fp, #-8] @将r3的值(2)写入 fp -8 的地方 i50 ldr r3, [fp, #-8] @将fp -8地址上的值(i = 2) 给r351 add r3, r3, #1 @ r3 = r3 + 1 = 352 str r3, [fp, #-8] @将r3的值(3)写入 fp -8 的地方 i53 ldr r0, [fp, #-8] @将fp -8地址上的值(i = 3) 给r054 ldr r3, [fp, #-8] @将fp -8地址上的值(i = 3) 给r355 add r3, r0, r3 @ r3 = r0 + r3 = 656 ldr r0, [fp, #-8] @将fp -8地址上的值(i = 3) 给r057 add r0, r0, #1 @r0 = r0 + 1 = 458 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传参数362 bl printf @调用printf打印63 ldr r1, .L2+4 @开始计算j了............64 ldr r2, [fp, #-12]65 ldr r3, [fp, #-12]66 add r2, r2, r367 ldr r3, [fp, #-12]68 add r3, r3, #169 str r3, [fp, #-12]70 ldr r3, [fp, #-12]71 add r3, r3, #172 str r3, [fp, #-12]73 ldr r3, [fp, #-12]74 add r3, r3, #175 str r3, [fp, #-12]76 ldr r3, [fp, #-12]77 add r3, r3, #178 str r3, [fp, #-12]79 ldr r0, [fp, #-12]80 ldr r3, [fp, #-12]81 add r3, r0, r382 mov r0, r183 mov r1, r284 mov r2, r385 bl printf86 mov r3, #087 mov r0, r388 sub sp, fp, #489 ldmfd sp!, {fp, pc}90 .L3:91 .align 292 .L2:93 .word .LC094 .word .LC195 .size main, .-main96 .ident "GCC: (ctng-1.8.1-FA) 4.5.1"97 .section .note.GNU-stack,"",%progbits
汇编代码解析:
首先刨去没用的信息,直接从 31行开始看
33~35行都是对栈指针的一些偏移和保存。
从以上汇编代码看可以看出简单的两句C编译成汇编就是一大坨,如果用纯汇编写15行左右应该就能搞定 执行效率几乎是C的20倍……………………