简单的两数交换不简单(C语言版)

第一种.使用临时变量来保存一个数进行交换:

int main()
{
    int a = 21;
    int b = 7;
    
    int tmp = a;
    a = b;
    b = tmp;
    
    return 0;
}
第二种. 只用算数运算符加减就可以进行交换的方式:

int main()
{
    int a = 21;
    int b = 7;

    a = a+b;
    b = a-b;
    a = a-b;

    return 0;
}

总结:

        这种方法在思想上讲十分的巧妙,不过在计算机的世界里这并不是一个好的方法,考虑一点编译后的问题,我们就会碰到溢出的问题,导致得到错误的结果。

高手们又使用位运算的异或运算来消除临时变量的方法,还不用担心溢出的问题。

第三种.使用异或运算来实现:

int main()
{
    int a = 21;
    int b = 7;
    
    a ^= b;
    b ^= a;
    a ^= b;


    return 0;
}
也可以简写成:

int main()
{
    int a = 21;
    int b = 7;
    
    a ^= b ^=a ^= b;

    return 0;
}
在计算机的世界里,上面的两种节省空间的方法:算数法和异或法虽然节省了一个微不足道的空间,但是却会浪费时间。效率上还不及临时变量法,更不要提其中出现的如溢出等隐蔽的问题了。使用
Ider$ gcc -S swap.c
我们来看看上面编编译成汇编文件的命令行数,对比上列四种方法的汇编命令行数:

tmp方式:


        .file   "swap.c"
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        movl    $21, -4(%rbp)
        movl    $7, -8(%rbp)
        movl    -4(%rbp), %eax
        movl    %eax, -12(%rbp)
        movl    -8(%rbp), %eax
        movl    %eax, -4(%rbp)
        movl    -12(%rbp), %eax
        movl    %eax, -8(%rbp)
        movl    $0, %eax
        leave
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"

        .section        .note.GNU-stack,"",@progbits

加法方式:

     

                  .file   "swap.c"
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        movl    $21, -4(%rbp)
        movl    $7, -8(%rbp)
        movl    -8(%rbp), %eax
        addl    %eax, -4(%rbp)
        movl    -8(%rbp), %eax
        movl    -4(%rbp), %edx
        movl    %edx, %ecx
        subl    %eax, %ecx
        movl    %ecx, %eax
        movl    %eax, -8(%rbp)
        movl    -8(%rbp), %eax
        subl    %eax, -4(%rbp)
        movl    $0, %eax
        leave
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
        .section        .note.GNU-stack,"",@progbits

异或方式:


        .file   "swap.c"
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        movl    $21, -4(%rbp)
        movl    $7, -8(%rbp)
        movl    -8(%rbp), %eax
        xorl    %eax, -4(%rbp)
        movl    -4(%rbp), %eax
        xorl    %eax, -8(%rbp)
        movl    -8(%rbp), %eax
        xorl    %eax, -4(%rbp)
        movl    $0, %eax
        leave
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
        .section        .note.GNU-stack,"",@progbits

异或的简单版方式:

        .file   "swap.c"
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        movl    $21, -4(%rbp)
        movl    $7, -8(%rbp)
        movl    -8(%rbp), %eax
        xorl    %eax, -4(%rbp)
        movl    -4(%rbp), %eax
        xorl    %eax, -8(%rbp)
        movl    -8(%rbp), %eax
        xorl    %eax, -4(%rbp)
        movl    $0, %eax
        leave
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"
        .section        .note.GNU-stack,"",@progbits

注:一行的异或代码和三行的异或代码编译成汇编指令时,他们的指令数都是一样的。从汇编指令的行数,利用临时变量的代码要明显短于其它两个。对于临时变量法,每次赋值只要读取一个变量的值到寄存器,然后再从寄存器写回到另一个变量中即可。但是对于运算操作,每次都需要读取两个数据到寄存器种,再进行运算操作。之后把结果写回到变量中。所以以后还是不要用这些看似很炫的方式来做两数交换了,还是老老实实用个临时变量。

                               




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值