题目描述
求1+2+3+…+n,
要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
样例输入
3
5
样例输出
6
15
使用等差数列公式n(1+n)/2怎么按照条件来实现算法呢?
#include <stdio.h>
#include <stdint.h>
int sum(int n)
{
return (uintptr_t)( ( &((uint8_t (*) [n])0)[1+n][0]) ) >> 1;
}
int main()
{
printf("%d\n",sum(5));//sum(5)=15
return 0;
}
具体解释如下,有点儿类似kernel里面使用container_of的感觉,但是这个(uint8_t (*) [n])0更加的精妙和讨巧,实在是高。
(uintptr_t)((&((uint8_t (*) [n])0)[1+n][0]))
分别来拆解
1.(uint8_t () [n])0),我们知道(uint8_t ())0)这个是将0转化为uint8_t 数据类型,并且sizeof(uint8_t) = 1,那么(uint8_t () [n])0)表示就是申明为一个数组指针并且每行数据相比前一行数据长度要多n*sizeof(uint8_t),内存起始地址为0,每列数据地址长度增加一个sizeof(uint8_t).实际就是行地址间隔从原来的sizeof(uint8_t)变成n*sizeof(uint8_t)
代码验证如下
for(i = 0; i < 5; i++) {
for(j = 0;j < 5 ;j++) {
printf("%4lu",(uintptr_t)(&((uint8_t (*)[5])0)[i][j]));
}
printf("\n");
}
打印结果如下:
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
15 16 17 18 19
20 21 22 23 24
如果是uint16_t的话,由于sizeof(uint16_t)=2,所以上面每列地址数值相差2,每行相差10,如下:
0 2 4 6 8
10 12 14 16 18
20 22 24 26 28
30 32 34 36 38
40 42 44 46 48
对于(uintptr_t)((&((uint8_t (*) [n])0)[i]),(i=0~5,n=5):
0
5
10
15
20
对于(uintptr_t)((&((uint8_t (*))0)[i]),(i=0~5,n=5),最常用的:
0
1
2
3
4
所以推理出了(uint8_t (*) [n])0,将行地址间隔增大了n*sizeof(uint8_t)。
2.&((uint8_t (*) [n])0)[1+n][0]) 相对0地址,偏移(1+n)*n地址长度,(1+n)*1是个数,如果[1+n][1],则(1+n)*2个数。
3.(uintptr_t)( ( &((uint8_t () [n])0)[1+n][0]) ) 将偏移地址转化为可以输出的实际数值,即n(1+n),然而n(n+1)/2,就是1+2+3+…+n之和,问题得解。
4.所以能够知道[1+n][0]就是n=5,就是第6行第1列的数据为30,除以2就是sum(5)=15的数值了。
5.(uintptr_t)( ( &((uint8_t (*) [n])0)[1+n][0]) ) >> 1就是(1+n)*n/2就是1+2+3+….+n之和。
绝对是妙不可言啊!!!上面是&((type () [n])0)的妙用,同时明白更加常用的&((type ())0)
6.下面也是一个函数指针的递归调用,完美的解决了题目所需的要求:
typedef unsigned int(*fun)(unsigned int);
unsigned int Solution(unsigned int n)
{
return 0;
}
unsigned int Sum_Solution(unsigned int n)
{
static fun f[2] = { Solution, Sum_Solution };
return n + f[!!n](n - 1);
}
世上无难事只怕有心人,更怕脑容量不够,不肯思索!!!