我找到了一个计算数字平方的函数:
int p(int n) {
int a[n]; //works on C99 and above
return (&a)[n] - a;
}
返回n 2 的值。问题是,它是如何做到的?经过一些测试后,我发现(&a)[k]
和(&a)[k+1]
之间的距离为sizeof(a)
/ sizeof(int)
。那是为什么?
答案 0 :(得分:117)
显然是一种黑客......但是一种不使用*
运算符来平算数字的方法(这是编码竞赛要求)。
(&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
答案 1 :(得分:86)
要理解这个hack,首先需要了解指针差异,即当指向相同数组元素的两个指针被减去时会发生什么?
当从另一个指针中减去一个指针时,结果是指针之间的距离(在数组元素中测量)。因此,如果p
指向a[i]
而q
指向a[j]
,则 p - q
等于i - j
。
当减去两个指针时,两者都指向同一个数组对象的元素,或者指向数组对象的最后一个元素的元素; 结果是两个数组元素的下标差异。 [...]。
换句话说,如果表达式P
和Q
分别指向数组对象的i
- 和j
- 元素,表达式如果值适合(P)-(Q)
类型的对象,则i−j
的值为ptrdiff_t
。
现在我希望你知道数组名称转换为指针,a
转换为指向数组a
的第一个元素的指针。 &a
是整个存储器块的地址,即它是数组a
的地址。下图将帮助您理解( read this answer以获取详细说明):
这有助于您了解为什么a
和&a
具有相同的地址以及(&a)[i]
如何是i th 数组的地址(同一地址)大小与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
。
答案 2 :(得分:35)
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]
或a
即可n
。
当然这是技术上未定义的行为,即使它在你的机器上工作,因为(&a)[n]
没有指向数组内部或者指向最后一个数组元素(根据指针算术的C规则的要求)。
答案 3 :(得分:12)
如果你有两个指针指向同一个数组的两个元素,那么它的差异将产生这些指针之间的元素数量。例如,此代码段将输出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
。
答案 4 :(得分:4)
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