zoukankan      html  css  js  c++  java
  • Add函数求两个数的和,不能使用+号或其它算术运算符

    转自:http://hawstein.com/posts/20.1.html

    Write a function that adds two numbers. You should not use + or any arithmetic operators.

    译文:

    写一个Add函数求两个数的和,不能使用+号或其它算术运算符。

    解答

    为了解决这个问题,让我们来深入地思考一下,我们是如何去加两个数的。为了易于理解, 我们考虑10进制的情况。比如我们要算759 + 674,我们通常从最低位开始加, 考虑进位;然后加第二位,考虑进位…对于二进制,我们可以使用相同的方法, 每一位求和,然后考虑进位。

    能把这个过程弄得更简单点吗?答案是YES,我们可以把求两个数的和分成两步, “加"与"进位",看例子:

    1. 计算759 + 674,但不考虑进位,得到323。

    2. 计算759 + 674,只考虑进位,而不是去加每一位,得到1110。

    3. 把上面得到的两个数加起来(这里又要用到加,所以使用递归调用即可)

    由于我们不能使用任何算术运算符,因此可供我们使用的就只有位运算符了。 于是我们把操作数看成二进制表示,然后对它们做类似的操作:

    1. 不考虑进位的按位求和,(0,0),(1,1)得0,(1,0),(0,1)得1, 使用异或操作可以满足要求。

    2. 只考虑进位,只有(1,1)才会产生进位,使用按位与可以满足要求。 当前位产生进位,要参与高一位的运算,因此按位与后要向左移动一位。

    3. 递归求和,直到进位为0

    代码如下:

    int Add2(int a, int b){
        if(b == 0) return a;
        int sum = a ^ b; // 各位相加,不计进位
        int carry = (a & b) << 1; // 记下进位
        return Add2(sum, carry); // 求sum和carry的和
    }
    

    递归的迭代版本如下:

    int Add3(int a, int b){
        while(b != 0){
            int sum = a ^ b;
            int carry = (a & b) << 1;
            a = sum;
            b = carry;
        }
        return a;
    }
    

    对于这道题目,还有一个非常巧妙的解法。我们知道,数组操作本质上其实是指针操作。 数组名其实是指向数组首元素地址的指针。比如说整数数组a,a[1]表示的是数组中的第 1个元素,这是一直以来我们的理解。而编译器看到a[1],它是怎么去理解的呢?

    首先,它会用数组首元素地址,加上偏移量,得到目标数据的地址, 然后再把里面的数据按指针指向类型的大小取出。所以,当编译器看到a[1], 它实际上做了下面的事:假设a指向的地址为0xbfc86d98

    得到目标数据地址:0xbfc86d98 + sizeof(int) * 1 = 0xbfc86d9c
    取出0xbfc86d9中的数据
    

    从上面可以看出,操作数组元素其实隐含了加法!所以呢,如果我们要求两个数的和, 只需要把其中一个看成地址,另一个看成偏移量,然后用返回它们对应数组元素的地址即可。 看代码:

    int Add1(int a, int b){
        char *c = (char*)a;
        return (int)&c[b]; // c+sizeof(char)*b=a + b
    }
    

    上述代码将a强制转换为指向char的指针c,然后返回c[b]的地址即可。c[b] 的地址就等于c + sizeof(char)*b = a + b。有人会问,它b是负数时OK吗? OK,没问题的。它代表偏移量为负,往反方向计算地址就是了。

    由于编译器对数组的解释方式如上所述,因此上面代码中的c[b]也可以写成b[c],或 a[5]可以写成5[a],效果是一样的,因为编译器都会先去求地址和偏移量的和。

    如果还想知道更多关于c语言的奇奇怪怪的点,推荐阅读《C陷阱与缺陷》。

  • 相关阅读:
    软件编写和设计中的18大原则
    Ubuntu CTRL+ALT+F1~F6 进入命令模式后不支持中文显示的解决办法
    BM串匹配算法
    KMP串匹配算法解析与优化
    mongodb随机查询一条记录的正确方法!
    这真的该用try-catch吗?
    计算机的本质与数值、文字、声音、图像
    编程语言的概念
    linux服务方式启动程序脚本(init.d脚本)
    linux的7种运行级别
  • 原文地址:https://www.cnblogs.com/cheng07045406/p/3271488.html
Copyright © 2011-2022 走看看