最近因为要换工作,所以也不时去搜索一些面试题看一下。下面两道题都是很常见的题,但却隐藏了一些陷阱。
1. 不使用中间变量,交换两个int型的值
这道题网上很常见,通常可能会给出下面这种答案:
a ^= b ^= a ^= b;
运行一下,结果可能是正确的。
哦!为什么是可能呢?
这里,我们应该首先介绍一个概念:序列点(sequence point)。ANSI C告诉我们:序列点是一个时间点(在整个表达式全部计算完毕之后或在 ||、 &&、 ? : 或逗号 运算符处, 或在函数调用之前), 此刻尘埃落定, 所有的副作用都已确保结束。并且,更加重要的是:在上一个和下一个序列点之间, 一个对象所保存的值至多只能被表达式的计算修改一次。
在这里,a被修改了两次,所以它是无定义的。
那么,我们可以修改一下:
a ^= b;
b ^= a;
a ^= b;
此时,序列点的问题就消除了,但是,还是有问题。
当a 与 b相同时,计算完成后,a = b = 0!这可不是我们想要的。
所以我们必须要加上额外的判断语句,完整的程序可能是下面这样的:
void exchange(int *a,int *b)
{
if(*a == *b)
return;
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
看上去,它要比使用中间变量的版本更加复杂,并且,没有证据表明这个版本有什么性能上的优势。
2. 不使用if,switch等条件判断语句,比较两个int型值的大小。
这个也比较常见,有这样一种可能的答案:
int max = (a + b + abs(a - b)) / 2;
看上去很巧妙,是的,但它忽略了一个问题:a+b 与a -b 都可能产生溢出,从而得出完全错误的结果。
事实上,使用>,<这些比较符是最聪明的做法,当然,这里我们需要使用其他方法来得出一个结果。
一种可能的方法是:
long long int max = ((long long)a + b + llabs((long long)a - b))/2;
/* long long is support by C99 */
通过使用更长的类型,可以保证得到正确的结果,但显然很麻烦:)
这两个问题都很简单,但是给出的答案都有一些缺陷,我们也可以总结出几条教训。
1. 一行代码只完成一件事。
2. 使用巧妙的代码要谨慎,简单清楚地代码更可靠。
参考: