无法理解这种方式来计算一个数字的平方
我find了一个函数来计算一个数字的平方:
int p(int n) { int a[n]; //works on C99 and above return (&a)[n] - a; }
它返回n 2的值。 问题是,它是如何做到的? 经过一些testing,我发现(&a)[k]
和(&a)[k+1]
是sizeof(a)
/ sizeof(int)
。 这是为什么?
显然是一个黑客…而是一个不使用*
运算符的方法(这是一个编码竞赛要求)。
(&a)[n]
相当于一个指向int
的指针
(a + sizeof(a[n])*n)
因此整个expression是
(&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
为了理解这个黑客攻击,首先你需要了解指针差异,即当两个指向相同数组元素的指针被减去时会发生什么?
当从另一个指针中减去一个指针时,结果就是指针之间的距离(以数组元素测量)。 所以,如果p
指向a[i]
并且q
指向a[j]
,那么p - q
等于i - j
。
C11:6.5.6附加操作符(p9):
当减去两个指针时 ,都应指向同一个数组对象的元素,或指向数组对象的最后一个元素的元素; 结果是两个数组元素的下标差异 。 […]。
换句话说,如果expression式P
和Q
分别指向数组对象的第i
个和第j
个元素, 则expression式(P)-(Q)
具有值i−j
只要该值适合于ptrdiff_t
types的对象。
现在我期待着你知道数组名称转换为指针, 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
数组。 因此,总arrays元素是n*n
= n
2 。
注意:
C11:6.5.6附加操作符(p9):
当减去两个指针时, 都应指向同一个数组对象的元素,或指向数组对象的最后一个元素的元素 ; 结果是两个数组元素的下标差异。 结果的大小是实现定义的 ,其types(有符号整数types)是在
<stddef.h>
头文件中定义的ptrdiff_t
。 如果结果在该types的对象中不可表示,则行为是未定义的。
由于(&a)[n]
既不指向相同数组对象的元素也不指向数组对象的最后一个元素, (&a)[n] - a
将调用未定义的行为 。
还要注意,最好将函数p
的返回types更改为ptrdiff_t
。
a
是n
int
的(可变)数组。
&a
是一个指向n
int
)的(variables)数组的指针。
(&a)[1]
是最后一个数组元素之后的int
int
指针。 这个指针是&a[0]
之后的n
int
元素。
(&a)[2]
是两个数组中最后一个数组元素的int
指针。 这个指针是&a[0]
之后的2 * n
int
元素。
(&a)[n]
是n
数组中最后一个数组元素的int
指针。 该指针是&a[0]
之后的n * n
int
元素。 只需要减去&a[0]
或者你有n
。
当然这在技术上是未定义的行为,即使它在你的机器上工作,因为(&a)[n]
不指向数组内部或指向最后一个数组元素(如指针算术C规则所要求的)。
如果你有两个指向同一个数组的两个元素的指针,那么它的差异将会产生这些指针之间的元素数。 例如,这段代码将输出2。
int a[10]; int *p1 = &a[1]; int *p2 = &a[3]; printf( "%d\n", p2 - p1 );
现在让我们考虑expression
(&a)[n] - a;
在这个expression式中, a
types是int *
并指向它的第一个元素。
expression式&a
types为int ( * )[n]
并指向映像二维数组的第一行。 它的值匹配a
虽然types不同的值。
( &a )[n]
是这个成像的二维数组的第n个元素,并且具有typesint[n]
即它是成像数组的第n行。 在expression式(&a)[n] - a
它被转换为它的第一个元素的地址并且具有types“int *”。
所以在(&a)[n]
和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的types是
int
指针
现在expression式(&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