这是一个适用于 amd64(又名 x86_64)或任何其他 64 位系统的解决方案,其中高 16 位地址位未使用(代码中未检查此要求!)。
#include <cassert>
#include <cstring>
#include <iostream>
// swap two pointers without using any additional space
// this is really a terrible idea and should never be used
template <typename T>
void ptr_swap(T*& p1, T*& p2)
{
// this only works on amd64, where the high 16 address bits are unused
static_assert(sizeof(T*) == 8, "only works on amd64");
// swap Nth pair of bytes
#define PTR_SWAP_PAIR(N) \
memcpy((char*)&p1 + 6, (char*)&p2 + N, 2); \
memcpy((char*)&p2 + N, (char*)&p1 + N, 2); \
memcpy((char*)&p1 + N, (char*)&p1 + 6, 2); \
PTR_SWAP_PAIR(0);
PTR_SWAP_PAIR(2);
PTR_SWAP_PAIR(4);
// restore amd64 pointer invariant (sign extension)
p1 = (T*)(((intptr_t)p1 << 16) >> 16);
}
int main()
{
int a = 1234567890;
int b = 999777555;
int* pa = &a;
int* pb = &b;
ptr_swap(pa, pb);
assert(pa == &b);
assert(pb == &a);
std::cout << *pa << '\n';
std::cout << *pb << '\n';
}
关于地址高 16 位可用作暂存器的参考:Using the extra 16 bits in 64-bit pointers
如果你对上面生成的汇编代码感到好奇:
movzx eax, WORD PTR [rsi]
mov WORD PTR [rdi+6], ax
movzx eax, WORD PTR [rdi]
mov WORD PTR [rsi], ax
movzx eax, WORD PTR [rdi+6]
mov WORD PTR [rdi], ax
movzx eax, WORD PTR [rsi+2]
mov WORD PTR [rdi+6], ax
movzx eax, WORD PTR [rdi+2]
mov WORD PTR [rsi+2], ax
movzx eax, WORD PTR [rdi+6]
mov WORD PTR [rdi+2], ax
movzx eax, WORD PTR [rsi+4]
mov WORD PTR [rdi+6], ax
movzx eax, WORD PTR [rdi+4]
mov WORD PTR [rsi+4], ax
movzx eax, WORD PTR [rdi+6]
mov WORD PTR [rdi+4], ax
mov eax, 16
shlx rax, QWORD PTR [rdi], rax
sar rax, 16
mov QWORD PTR [rdi], rax
相对于使用std::swap():
mov rax, QWORD PTR [rdi]
mov rdx, QWORD PTR [rsi]
mov QWORD PTR [rdi], rdx
mov QWORD PTR [rsi], rax
是的,太可怕了。
编辑:这是@Ajay 的回答生成的程序集:
mov rax, QWORD PTR [rdi]
xor rax, QWORD PTR [rsi]
mov QWORD PTR [rdi], rax
xor rax, QWORD PTR [rsi]
mov QWORD PTR [rsi], rax
xor QWORD PTR [rdi], rax
与std::swap()相比,它花费了两条额外的指令并节省了一个寄存器(rdx)。