给定两个整数a和b,我们如何在不使用+,-,++,-,…等运算符的情况下求和a + b?
一种有趣的方式是:
// May not work with C++ compilers and
// may produce warnings in C.
// Returns sum of 'a' and 'b'
int sum(int a, int b)
{
char *p = a;
return (int)&p[b];
}
尽管乍一看很尴尬,但我们可以轻松地了解正在发生的事情。首先,我们创建了一个指针p。指针的值是一个内存地址。在这种情况下,p的值是地址a。
请记住:p指向位置a。在该示例中,如果我们想知道位置a(999)的值,请询问* p。如果我们想知道变量a(41)的地址,请询问&a。
如果我们评估p [b],我们将获得位置p + b的内存值。实际上,我们对&p [b]求值,女巫等于在不访问其值的情况下获取地址p + b。由于p = a,&p [b]将返回地址a + b。
我们不想返回内存地址(int *)。我们要返回一个整数(int)。因此,我们将&p [b]转换为int。
如果将p的类型从char *更改为int *会发生什么?让我修正我之前说的内容:当我们评估p [b]时,我们不会评估p + b。我们评估p + sizeof(* p)* b。为什么?想象一下这个例子:int类型的变量在内存中占据四个位置。
sizeof(* p)考虑每个变量在内存中占据的位置数量。
我们要评估p + b。换句话说,我们希望sizeof(* p)等于1。因此,如果* p是一个字符,我们很高兴。
让我们看一下总和的真值表(现在暂时忽略进位):
仔细看,我们发现总和与异或的真值表是相同的。仅当输入不同时为1。
现在,我们如何检测进位?让我们看一下进位的真值表。
仔细观察,我们发现总和与异或的真值表是相同的。仅当输入不同时为1。
现在,我们如何检测进位?让我们看一下进位的真值表。
再仔细看一次,我们注意到进位的和逻辑与的真值表是相同的。现在,我们必须将a&1向左移动并求和a ^ b。但是,这些操作也可能带有进位。没问题,只需将a ^ b与a和1递归左移即可。
// Returns sum of a and b using bitwise
// operators.
int sum(int a, int b)
{
int s = a ^ b;
int carry = a & b;
if (carry == 0) return s;
else return sum(s, carry << 1);
}
此解决方案已在此处讨论。
让我们记住一些事实:
- printf返回成功打印的字符数。
- 说明符%* c请求两个参数:第一个是自定义宽度,第二个是字符。例如,printf(“%* c”,5,’a’)将打印“ a”。
- 特殊字符’\ r’从输出字符串的开头返回光标。例如,printf(“ abcd \ r12”)将打印“ 12cd”。
记住这一点,我们可以理解此函数:
// Returns sum of a and b using printf
// Constraints: a, b > 0.
int sum(int a, int b)
{
return printf("%*c%*c", a, '\r', b, '\r');
}
此解决方案已在此处讨论。