135

我找到了一个计算数字平方的函数:

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)。这是为什么?

4

5 回答 5

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
于 2015-01-07T21:30:10.460 回答
86

要理解这个hack,首先你需要理解指针的区别,即当两个指向同一个数组元素的指针相减时会发生什么?

当一个指针从另一个指针中减去时,结果是指针之间的距离(以数组元素测量)。因此,如果p指向a[i]q指向a[j],则p - q等于i - j

C11:6.5.6 加法运算符(p9):

当两个指针相减时,都应指向同一个数组对象的元素,或者指向数组对象的最后一个元素;结果是两个数组元素的下标之差。[...]。
换句话说,如果表达式PQ分别指向数组对象的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


笔记:

C11:6.5.6 加法运算符(p9):

当两个指针相减时,两者都应指向同一个数组对象的元素,或者指向数组对象的最后一个元素;结果是两个数组元素的下标之差。结果的大小是实现定义的,其类型(有符号整数类型)ptrdiff_t在标头中定义<stddef.h>。如果结果在该类型的对象中不可表示,则行为未定义。

由于(&a)[n]既不指向同一数组对象的元素,也不指向数组对象的最后一个元素,(&a)[n] - a将调用未定义的行为

另请注意,最好将函数的返回类型更改pptrdiff_t.

于 2015-01-07T23:59:19.800 回答
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]or就a可以了n

当然,这在技术上是未定义的行为,即使它在您的机器上工作,因为(&a)[n]它不指向数组内部或不指向最后一个数组元素(根据 C 指针算术规则的要求)。

于 2015-01-07T21:29:19.450 回答
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

于 2015-01-07T21:35:49.063 回答
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

因此,

  1. 类型(&a)[n]int[n]指针
  2. 类型aint指针

现在表达式(&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
于 2015-01-14T03:14:36.673 回答