我找到了一个计算数字平方的函数:
int p(int n) {
int a[n]; //works on C99 and above
return (&a)[n] - a;
}
它返回 n 2的值。问题是,它是如何做到的?经过一番测试,我发现(&a)[k]and之间(&a)[k+1]是sizeof(a)/ sizeof(int)。这是为什么?
我找到了一个计算数字平方的函数:
int p(int n) {
int a[n]; //works on C99 and above
return (&a)[n] - a;
}
它返回 n 2的值。问题是,它是如何做到的?经过一番测试,我发现(&a)[k]and之间(&a)[k+1]是sizeof(a)/ sizeof(int)。这是为什么?
显然是一种黑客行为......但是一种无需使用*运算符即可对数字进行平方的方法(这是编码竞赛的要求)。
(&a)[n]
相当于一个指向int位置的指针
(a + sizeof(a[n])*n)
因此整个表达式是
(&a)[n] -a
= (a + sizeof(a[n])*n -a) /sizeof(int)
= sizeof(a[n])*n / sizeof(int)
= sizeof(int) * n * n / sizeof(int)
= n * n
要理解这个hack,首先你需要理解指针的区别,即当两个指向同一个数组元素的指针相减时会发生什么?
当一个指针从另一个指针中减去时,结果是指针之间的距离(以数组元素测量)。因此,如果p指向a[i]和q指向a[j],则p - q等于i - j。
当两个指针相减时,都应指向同一个数组对象的元素,或者指向数组对象的最后一个元素;结果是两个数组元素的下标之差。[...]。
换句话说,如果表达式P和Q分别指向数组对象的i第 -th 和-th 元素,则表达式具有值适合类型对象的值。j(P)-(Q)i−jptrdiff_t
现在我希望您知道数组名称转换为指针,a转换为指向数组第一个元素的指针a。&a是整个内存块的地址,即它是数组的地址a。下图将帮助您理解(阅读此答案以获得详细说明):

这将帮助您理解为什么a和&a具有相同(&a)[i]的地址,以及第 i个数组的地址(与 的大小相同a)的地址如何。
所以,声明
return (&a)[n] - a;
相当于
return (&a)[n] - (&a)[0];
这种差异将给出指针(&a)[n]和之间的元素数量(&a)[0],它们是n每个元素的数组n int。因此,数组元素总数n*n= n2。
笔记:
当两个指针相减时,两者都应指向同一个数组对象的元素,或者指向数组对象的最后一个元素;结果是两个数组元素的下标之差。结果的大小是实现定义的,其类型(有符号整数类型)
ptrdiff_t在标头中定义<stddef.h>。如果结果在该类型的对象中不可表示,则行为未定义。
由于(&a)[n]既不指向同一数组对象的元素,也不指向数组对象的最后一个元素,(&a)[n] - a将调用未定义的行为。
另请注意,最好将函数的返回类型更改p为ptrdiff_t.
a是 的(变量)数组n int。
&a是指向 的(变量)数组的指针n int。
(&a)[1]int是最后一个int数组元素之后的指针。该指针是n int之后的元素&a[0]。
(&a)[2]是一个指针,指向int两个int数组的最后一个数组元素。该指针是2 * n int之后的元素&a[0]。
(&a)[n]int是数组的最后一个int数组元素之后的指针n。该指针是n * n int之后的元素&a[0]。只需减去&a[0]or就a可以了n。
当然,这在技术上是未定义的行为,即使它在您的机器上工作,因为(&a)[n]它不指向数组内部或不指向最后一个数组元素(根据 C 指针算术规则的要求)。
如果你有两个指针指向同一个数组的两个元素,那么它的差异将产生这些指针之间的元素数。例如,此代码段将输出 2。
int a[10];
int *p1 = &a[1];
int *p2 = &a[3];
printf( "%d\n", p2 - p1 );
现在让我们考虑表达式
(&a)[n] - a;
在这个表达式a中,有类型int *并指向它的第一个元素。
表达式&a具有类型int ( * )[n]并指向成像二维数组的第一行。a尽管类型不同,但它的值匹配的值。
( &a )[n]
是这个成像二维数组的第 n 个元素,并且具有类型int[n]即它是成像数组的第 n 行。在表达式(&a)[n] - a中,它被转换为它的第一个元素的地址,并且类型为 `int *。
所以在 (&a)[n]和之间a有 n 行 n 个元素。所以差值将等于n * n。
Expression | Value | Explanation
a | a | point to array of int elements
a[n] | a + n*sizeof(int) | refer to n-th element in array of int elements
-------------------------------------------------------------------------------------------------
&a | a | point to array of (n int elements array)
(&a)[n] | a + n*sizeof(int[n]) | refer to n-th element in array of (n int elements array)
-------------------------------------------------------------------------------------------------
sizeof(int[n]) | n * sizeof(int) | int[n] is a type of n-int-element array
因此,
(&a)[n]是int[n]指针a是int指针现在表达式(&a)[n]-a执行指针减法:
(&a)[n]-a
= ((a + n*sizeof(int[n])) - a) / sizeof(int)
= (n * sizeof(int[n])) / sizeof(int)
= (n * n * sizeof(int)) / sizeof(int)
= n * n