有一个面试题是两数之间不通过第三个数进行交换,该题的标准答案是:
a ^= b;
b ^= a;
a ^= b;
我一直在思考,为什么希望不通过第三个数来计算,好处到底是什么。为了研究清楚它的意义,我看了下反汇编代码
这个是异或交换的反汇编,共15条指令:
a ^= b;
00007FF7C3A30065 mov eax,dword ptr [b]
00007FF7C3A30068 mov ecx,dword ptr [a]
00007FF7C3A3006B xor ecx,eax
00007FF7C3A3006D mov eax,ecx
00007FF7C3A3006F mov dword ptr [a],eax
b ^= a;
00007FF7C3A30072 mov eax,dword ptr [a]
00007FF7C3A30075 mov ecx,dword ptr [b]
00007FF7C3A30078 xor ecx,eax
00007FF7C3A3007A mov eax,ecx
00007FF7C3A3007C mov dword ptr [b],eax
a ^= b;
00007FF7C3A3007F mov eax,dword ptr [b]
00007FF7C3A30082 mov ecx,dword ptr [a]
00007FF7C3A30085 xor ecx,eax
00007FF7C3A30087 mov eax,ecx
00007FF7C3A30089 mov dword ptr [a],eax
这个是传统的通过temp来交换的反汇编,共6条指令:
temp = a;
00007FF7C3A300DC mov eax,dword ptr [a]
00007FF7C3A300DF mov dword ptr [temp],eax
a = b;
00007FF7C3A300E5 mov eax,dword ptr [b]
00007FF7C3A300E8 mov dword ptr [a],eax
b = temp;
00007FF7C3A300EB mov eax,dword ptr [temp]
00007FF7C3A300F1 mov dword ptr [b],eax
从指令数量上看,异或交换并不占有优势。我写了个大循环代码测试
int a = 0x11, b = 0x110;
const int swap_times = 1e8;
ULONGLONG m;
m = GetTickCount64();
for (size_t i = 0; i < swap_times; i++)
{
a ^= b;
b ^= a;
a ^= b;
}
printf("异或交换,执行时间%llu毫秒\n", GetTickCount64() - m);
int temp;
m = GetTickCount64();
for (size_t i = 0; i < swap_times; i++)
{
temp = a;
a = b;
b = temp;
}
printf("中间变量交换,执行时间%llu毫秒\n", GetTickCount64() - m);
输出为:
异或交换,执行时间640毫秒
中间变量交换,执行时间313毫秒
看来异或交换时间上没有优势。从汇编代码上可以看出,它多用了一个寄存器ecx来取代栈上的中间变量temp,栈的功能是存储,栈空间是有限的,而寄存器是内存空间与逻辑运算单元之间的桥梁,不充当存储作用,也不存在空间占用的概念。所以我得到的结论是,异或交换的思想,就是用时间来换取空间。