用异或来交换两个变量更耗时

来源:互联网 发布:js的json字符串转数组 编辑:程序博客网 时间:2024/06/14 10:28

FROM:陈硕 http://blog.csdn.net/solstice/article/details/5166912 

翻转一个字符串,例如把 "12345" 变成 "54321",这是一个最简单的不过的编码任务,即便是 C 语言初学者的也能毫不费力地写出类似如下的代码:

 

// 版本一,用中间变量交换两个数,好代码

void reverse_by_swap(char* str, int n)

{

  char* begin = str;

  char* end = str + n - 1;

  while (begin < end) {

    char tmp = *begin;

    *begin = *end;

    *end = tmp;

    ++begin;

    --end;

  }

}

 

这个代码清晰,直白,没有任何高深的技巧。

 

不知从什么时候开始,有人发明了不使用临时变量交换两个数的办法,用“不用临时变量 交换 两个数”在 google 上能搜到很多文章。下面是一个典型的实现:

 

// 版本二,用异或运算交换两个数,烂代码

void reverse_by_xor(char* str, int n)

{

  // WARNING: BAD code

  char* begin = str;

  char* end = str + n - 1;

  while (begin < end) {

    *begin ^= *end;

    *end ^= *begin;

    *begin ^= *end;

    ++begin;

    --end;

  }

}

 

受一些过时的教科书的误导,有人认为程序里少用一个变量,节省一个字节的空间,会让程序运行更快。这是不对的,至少在这里不成立:

 

1.       这个所谓的“技巧”在现代的机器上只会更慢(我甚至怀疑它从来就不可能比原始办法快)。原始办法是两次内存读和写,这个"技巧"是六读三写加三次异或(或许编译器可以优化成两读三写加三次异或)。

2.       同样也不能节省内存,因为中间变量 tmp 通常会是寄存器(稍后有汇编代码供分析)。就算它在函数的局部堆栈(stack)上,反正栈已经开在那儿了,也没有进一步的函数调用,根本节约不了一丁点内存。

3.       相反,由于计算步骤较多,会使用更多的指令,编译后的机器码长度会增加。(这不是什么大问题,短的代码不一定快,后面有另外一个例子。)

 

这个技巧的意义完全在于应付变态的面试,所以知道就行,但绝对不能放在产品代码中。我也想不出问这样的面试题意义何在。

 

更有甚者,把其中三句:

    *begin ^= *end;

    *end ^= *begin;

    *begin ^= *end;

写成一句:

    *begin ^= *end ^= *begin ^= *end; // WRONG

(a^=b^=a^=b;)

这更是大有问题,会导致未定义的行为(undefined behavior)。C 语言的一条语句中,一个变量的值只允许改变一次,像 x = x++ 这种代码都是未定义行为。在C语言里没有哪条规则保证这两种写法是等价的。
(致语言律师:我知道,黑话叫序列点,一个语句可能不止一个序列点,请允许我在这里使用不精确的表述。)

 

这不是一个值得炫耀的技巧,只会丑化劣化代码。

0 0
原创粉丝点击