zoukankan      html  css  js  c++  java
  • 有趣的++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倍……………………

    深圳宝安华美居
  • 相关阅读:
    element:记一次重置表单引发提交数据为默认数据现象
    三种常用又简单的排序算法
    (07)GitHub从2021.08.13开始使用Token代替账号和密码
    does not implement methodSignatureForSelector:
    自用python库
    2048
    CCSP2021游记
    2021 CCPC 桂林站游记
    2021 ICPC 沈阳站游记
    2021SDU新生赛游记
  • 原文地址:https://www.cnblogs.com/tid-think/p/5136574.html
Copyright © 2011-2022 走看看