zoukankan      html  css  js  c++  java
  • 交换两变量值的一些邪门歪道

    面试奇葩——交换两变量值的一些邪门歪道

    交换两个变量的值,最常见的写法是

    1
    2
    3
    4
    5
    int i , j ;
    int temp ;
    temp = i ;
    i = j ;
    j = temp ;

      这种写法相信任何学过程序设计语言的都知道。

      然而有些着三不着两的极品面试官却喜欢追问,不用中间变量应该怎么写?这一追问不要紧,追出了很多邪门歪道,例如

    1
    2
    3
    4
    int i , j ;
    i = i + j;
    j = i - j ;
    i = i - j ;

      这样的写法,非常晦涩,但挡不住这样写的人为少定义了一个变量而洋洋得意。然而这样写真的行得通吗?运行一下下面的代码就明白了。 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #include <stdio.h>
    #include <limits.h>
     
    int main( void )
    {
        
      int i , j ;
      int temp ;
       
      i = INT_MAX ;
      j = INT_MAX - 1;
       
      puts("交换前:");
      printf("i = %d , j = %d " , i , j );
       
      temp = i ;
      i = j ;
      j = temp ;
       
      puts("交换后:");
      printf("i = %d , j = %d " , i , j );
           
      i = INT_MAX ;
      j = INT_MAX - 1;
       
      puts("交换前:");
      printf("i = %d , j = %d " , i , j );
       
      i = i + j ;
      j = i - j ;
      j = i - j ;
       
      puts("交换后:");
      printf("i = %d , j = %d " , i , j );
     
      return 0;
    }

      运行结果通常都是荒诞不经的。原因很简单, 计算 i + j 时发生溢出,而溢出在C语言中是一种未定义行为,所以得到什么样的荒诞结果都不奇怪。

      更极品的回答是用乘除法。 

    1
    2
    3
    i = i * j;
    j = i / j;
    i = i / j;

      这不明摆着更容易溢出吗?甚至会发生除以0的错误。可居然还有人认为“其实不然”,“不会溢出”。严重怀疑这是三鹿奶粉喝多了的后遗症。

      有的面试官可能自以为自己还没愚蠢到这种程度,因为他心目中的“标准答案”是通过异或运算。

    1
    2
    3
    i ^= j;
    j ^= i;
    i ^= j;

      这种写法虽然不会产生溢出,但使用条件受限,因为只能用于整数类型,浮点类型、结构体类型等完全不适用。而且除了少用了一个临时变量,没有其他任何好处。

      有些人天真地以为这种写法对于整数类型变量来说会更快,其实这种猜测毫无根据。很难说异或运算就比赋值快,据一份公开发表的数据,在某环境下,异或用时为0.050μs,而赋值运算只用0.036μs。

      就可读性而言,异或写法显然如同蝌蚪文天书。

      更有极品将异或写法发挥到了极限:

    1
    i ^= j ^= i ^= j;

      对于C语言来说,这种写法大错特错。因为C语言规定在两个序点之间同一个数据对象最多只能改变一次,否则就是未定义行为。(参见 “牙里长嘴”和“a+=a-=a*a” )这种代码压根就不可以写。如果运行结果碰巧正确,那么只是瞎猫碰上死耗子而已。况且已有报告称在某些环境下这种写法的运行结果是错误的。

      异或写法还有一个潜在的问题,比如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #include <stdio.h>
     
    void swap( int  * , int * );
     
    int main( void )
    {
        
      int i = 5 , j = 10 ;
       
      puts("交换前:");
      printf("i = %d , j = %d " , i , j );
       
      swap( & i , & j);
       
      puts("交换后:");
      printf("i = %d , j = %d " , i , j );
     
      //自己和自己交换
      puts("交换前:");
      printf("i = %d " , i );
       
      swap( & i , & i);
     
      puts("交换后:");
      printf("i = %d " , i );
     
      return 0;
    }
     
    void swap( int  * p , int * q )
    {
       * p ^= * q ;
       * q ^= * p ;  
       * p ^= * q ;  
    }

      这段代码运行的结果是:

    交换前:

    i = 5 , j = 10

    交换后:

    i = 10 , j = 5

    交换前:

    i = 10

    交换后:

    i = 0

      不难发现,如果swap()函数的两个实参为指向同一数据对象的指针时,结果是错误的。

      当然,你可能认为自己和自己交换有些无聊。但我们不能排除有些算法可能存在这种交换的可能性。

      除了这种可能性,还有另外一种可能性,那就是你把代码写错了,多写了一次不必要的变量自己和自己的交换。如果你用的是使用中间变量的算法,那么除了代码有瑕疵,运行结果的正确性还是有保证的。但如果你用的是异或的办法,软件灾难则是召之即来——保准比打车来的还快。

      所以结论就是,不要用异或的办法交换变量值。

      写到这里,本应打住。不过偶然兴起,心血来潮,随手一搜,竟发现还有更多的奇葩写法。

      某java-er竟然一本正经地提出下面两种方法: 

    1
    a = a + b - (b = a);
    1
    b = a + (a = b)*0; 

      我不清楚在java里这是否成立,但在C语言中,这都属于未定义行为。令人拍案的是,该java-er竟然洋洋得意地写到:

      "这是java语言写的,但是语言不分种类,任何方法都是通用的,推荐使用a=b+(b=a)*0和a^b的方法,有些环境也许会不通过,但是方法和思想不会错得"

      SHIT!

     
     
     
  • 相关阅读:
    给xml某个节点赋值
    把datatable的某些数据提取出来放在另一个表中
    投资技巧:抛股票有技巧 常用方法介绍
    jquery的实用技巧,非常实用
    我觉得需要关注和跟进的一些.net技术
    公司网站的架构
    uboot移植经历
    ARM处理器中CP15协处理器的寄存器
    uboot 学习 Makefile分析
    uboot移植
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3375602.html
Copyright © 2011-2022 走看看