我之前的博客里有讲到cpu是怎么做加减乘除的
偶然间看到一个面试题说的是不用加法去做加运算
其实仔细想想,就是以二进制的方式来运算咯,只不过把计算机组成原理里的那套搬到编程里来
复习一下加法:
cpu首先对加法的ADD指令是这样做的:
比如0001+0001
首先进行&运算等于0001+0001=0001 这里顺便复习一下与运算0&0=0,1&1=1,1&0或0&1=0
也就是说两数相等等于这个数,两数不相等等于0
进行与运算后,cpu有个进位条件,当两数为1时,就满足进位条件,所以需要做一个<<1的运算,除非两数为0则不做
这样就完成了一次运算,但是到这还没有结束
cpu还要检查一下其它的bit位有没有做完运算,所以cpu会把进位后的结果放到通用寄存器里存储着
如何判断其它bit位有没有做完运算?很简单,只需要进行一次xor运算就可以了
xor的运算是0任何数都等于任何数,而1任何数都等于任何数取反
cpu会将加数和被加数做一次xor运算,注意这里的加数与被加数不是进位后的数
0001^0001=0000
其结果会放在加数寄存器里
然后在把进位结果放到被加数寄存器里
那么此时加数寄存器=0000 而被加数寄存器=0010
cpu会判断一次被加数寄存器是否为0000如果不为0就代表还有位需要运算
那么cpu开始了第二次运算,重复上面的步骤
也就是 0000&0010
那么结果就是0000
如果是0000那么就不用进位了,直接进入下一个步骤
xor运算,注意结果被放入到了一个临时的通用寄存器里了,并不会放在加数与被加数的寄存器上,所以它们的值还是0000和0010
0000^0010=0010
这样看会更清晰吧:
0000
^^^^
0010
———
0010
0^任何数都等于任何数
然后在将结果放入到加法寄存器里,被加数寄存器里放入刚刚的临时通用寄存器里的结果0000
然后cpu判断被加数的bit位算完了就跳出循环
那么加法寄存器里就是答案
也成累加器寄存器
那么换算成c语言怎么换算呢?
1.函数:
int add(int a,int b);
a与b就是加数与被加数
这里我们直接使用,cpu的话会放入到累加器,这里我们省略一个变量
声明一个变量用来用临时的通用寄存器
int temp_reg = 0;
根据上面说的,是一个循环,每次循环判断被加数也就是b的进制位是否运算完,判断方法就是是否等于0,如果为0那么bit位都是0
while(b != 0){
}
根据上面说的,第一步做与运算然后把结果放入到临时寄存器里
temp_reg = a & b;
判断一下临时寄存器是否需要进位,上面说过进位的条件是bit位是否为0
如果每次&运算后bit位有一个不为0那么都需要进位一次
if(temp_reg != 0){
temp_reg = temp_reg << 1
}
然后根据上面说的做一次与运算,结果放入到加法寄存器里,这里你也可以在申请一个变量做累加器寄存器,这里我只是觉得这样写比较节省内存。因为cpu本身不是用加法寄存器保存这个数的,用的是累加器寄存器
a ^= b;
最后再把通用寄存器的结果放入到被加数寄存器里
b = temp_reg;
这样模拟cpu二进制加法运算的工作就完成了
最后一步直接return a即可
完整代码:
int add(int a, int b) {
unsigned int temp_reg=0;
while(b != 0){
temp_reg = a & b;
if(temp_reg != 0){
temp_reg=temp_reg<<1;
}
a ^= b;
b = temp_reg;
}
return a;
}
力扣:
还有一种写法
是利用编译器的解析器
但是这种方法是耍个小聪明原则上编译内部还是用了+符号
如,假设有一个char指针指向了一个包含’abcd’的字符串
char p;
那么p[2]编译器会翻译成(p+2) 这个是指针的用法
是解引用,对这个地址取值,而&则是取这个地址
所以当我们用指针的方式来对变量进行运算:
int a,int b;
(char)a[b]; 这样写可以让编译器帮我们隐式翻译成*(a+b)
那么问题是会对这块地址取值,解引用,但是这个地址是不安全的,因为这不是指针,实际地址就是a的值+b的值,但是这不是我们想要的,上面说了用&,只要在前面加一个&编译起会翻译成返回这块地址
也就是说把*(a+b)不对它解引用了,直接把它们当地址返回,就是返回a+b的值了
代码:
int add(int a,int b){
return (long)&(((char*)a)[b]);
}
注意返回的地址是指针,所以我们转换一下就可以了
这样的代码本质上还是使用了+