这是HTC笔试中碰到的一个很基础的题,可是波儿大人竟然不会做,可见波儿大人是有多水!知耻后勇,好生搜罗学习了一番。
下面的三种实现方法均来自小兵同学的文章《C语言实现交换两个数》,另外分别附加了俺的一丁点儿讲解笔记。
和差差法
void exchange(int *a, int *b)
{
*a = *a + *b;
*b = *a - *b;
*a = *a - *b;
}
最简单最易理解也最精妙!
异或位运算法
void exchange(int *a, int *b)
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
更简练地,可写成:
*a ^= *b^= *a ^= *b;
还不了解异或位运算的同学可看完下面这几段,跟俺一起脑补一下。大侠请绕过。
异或(exclusive OR)是数学上的一种逻辑运算,数学符号为“⊕”。缩写xor,也是计算机中常用的表示符号。但在C/C++语言中,用^表示异或位运算符。运算规则是:1^0=1, 1^1=0, 0^0=0。也就是半加运算,是相加不进位的二进制加法。所谓“异或”,就是位按逻辑或运算为真、且两数不相同时结果才为真。
异或算符最经典的用法是将一个数其中的几位按位取反,而其它位又保持不变。如将变量x的最后3位按位取反,而前面的位不变,则可用x=07^x。
那么,上面的语句完整写出即是:
*a1 = *a^*b;
*b1 = *b^*a1 = *b^(*a^*b) = *a^(*b^*b) = *a^0 = *a;
*a2 = *a1^*b1 = (*a^*b)^*a = *b^(*a^*a) = *b^0 = *b;
本质跟“和差差”法一样,都是先把两变量的总体信息保留在其中一个变量中,然后用该总变量信息结合那个还存在的分变量信息分别解析出原来的两个分量并实现交换赋值。区别是“和差差法”是通过加减运算“融合”->“拆解”,而“异或位运算法”则是通过异或运算。
内联汇编法
For Microsoft
//Microsoft C++:
int x = 12, y= 19;
_asm
{
push x;
push y;
pop x;
pop y;
}
printf("x is %d, y is %d /n", x,y);
内联汇编即是在C/C++代码中插入汇编语言的语法片段,片段通常由asm关键字后接一对括号语句段来插入。上面的 _asm
[2]是该关键字的Microsoft C++版本,在Linux C++中或者说gcc/GNU中与它对应的则是 __asm__ 或者 asm。下面是俺尝试编写的交换两变量的相应Linux汇编版本:
For Linux
//Linux C++:
#include <stdio.h>
int main()
{
int x = 12, y= 19;
asm(
"push x\n\t"
"push y\n\t"
"pop x\n\t"
"pop y\n\t"
);
printf("x is %d, y is %d /n", x,y);
return 0;
}
需要说明的是该Linux版目前暂未能通过gcc的编译。编译报错为:
$ gcc asm.C -o asm
/tmp/user/ccKNLY8z.o(.text+0x2c): In function `main':
: undefined reference to `x'
…
俺真心不会汇编,临时抱佛脚照猫画虎
[3]。大侠路过还盼指点!
参考资料
[0] 小兵, C语言实现交换两个数
[1] jocks, 为什么异或运算可以实现两个整数的交换,而无需借助第3个临时变量
[2] MS Developer Network, __asm
[3] dolinux, c内联汇编