我的印象是,当我们提供接近最大整数限制的输入时,只使用X-OR进行交换。令人惊讶的是,使用加法和减法的这种简单交换也适用于那些输入。
#include <iostream>
void simple_swap(int &x, int &y) {
x += y;
y = x - y;
x -= y;
}
int main() {
int a = 10; int b = 15;
/* Testing integer overflow, sum has 1 bit outside of 4-bytes integer limit */
a = 0xFFFFFFFF; // a is 2^32-1 = 4294967295
b = 0xFFFFFFFE; // b is 2^32-2 = 4294967294
std::cout << "Before swap " << "a = " << (unsigned int)a << ", b=" << (unsigned int)b << std::endl;
simple_swap(a, b);
std::cout << "After swap " << "a = " << (unsigned int)a << ", b=" << (unsigned int)b << std::endl;
return 0;
}
我正在使用Visual Studio 2013.如果您使用的是其他编译器且代码无法正常显示输出,请在评论中告诉我。
我有兴趣了解这背后的原因。编译器是否用其他东西替换代码?
答案 0 :(得分:5)
即使它似乎有用也是一个坏主意。
void simple_swap(int &x, int &y) {
x += y;
y = x - y;
x -= y;
}
如果x
和y
都非常大或非常小,您将获得整数溢出/下溢,这是未定义的行为。这意味着即使它似乎对你有用,也没有任何保证它可以为其他人工作,甚至在你下次编译时也是如此。它可以从一个时刻突破到另一个时刻。
同样仅仅是为了记录,使用xor进行交换也被打破了,但出于完全不同的原因。
void simple_swap(int &x, int &y) {
x ^= y;
y ^= x;
x ^= y;
}
大部分时间都有效,但如果您尝试将变量与自身交换,则会破坏:
simple_swap(a, a);
void simple_swap(int &x, int &y) { // both x and y refer to the same variable (a).
x ^= y; // both variables will be 0 after this statement.
y ^= x;
x ^= y;
}
现在用自己交换一个变量可能看起来很愚蠢而且不太可能,但你并不总是知道。它可能是两个指针恰好指向同一个变量,突然间你得到了一个奇怪的错误。为了确保它始终有效,你必须使它过于复杂并测试两个参数实际上并不相同。
void simple_swap(int &x, int &y) {
if ( x != y) {
x ^= y;
y ^= x;
x ^= y;
}
}
比这样一个简单的函数更难读,而不是像最初想的那么简单。
只需使用简单易行的方法,不要试图聪明:
void simple_swap(int &x, int &y) {
int temp = x;
x = y;
y = temp;
}
交换很简单,每个编译器都能够将其优化到最佳版本。
答案 1 :(得分:0)
通过操作,数学值导致正确的交换,忽略溢出的可能性。
在2的补码中使用的运算维持不变量,即值是“真正的数学值”模2 ^(word_size_in_bytes)。
由于任何“真正的数学值”只有一个模2 ^(字大小的字节),所以保证交换交换值(无论它们代表什么)。
当然,C或C ++标准不保证2的补码。这就是在&gt; 99.9999%的CPU中完成的工作。