我找到了一个计算数字平方的函数:
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−j
ptrdiff_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
= n
2。
笔记:
当两个指针相减时,两者都应指向同一个数组对象的元素,或者指向数组对象的最后一个元素;结果是两个数组元素的下标之差。结果的大小是实现定义的,其类型(有符号整数类型)
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