无法通过这种方式来计算数字的平方

时间:2015-01-07 21:18:21

标签: c arrays pointers c99 variable-length-array

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

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

5 个答案:

答案 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

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

  

当减去两个指针时,两者都指向同一个数组对象的元素,或者指向数组对象的最后一个元素的元素; 结果是两个数组元素的下标差异。 [...]。
  换句话说,如果表达式PQ分别指向数组对象的i - 和j - 元素,表达式如果值适合(P)-(Q)类型的对象,则i−j的值为ptrdiff_t

现在我希望你知道数组名称转换为指针,a转换为指向数组a的第一个元素的指针。 &a是整个存储器块的地址,即它是数组a的地址。下图将帮助您理解( read this answer以获取详细说明):

enter image description here

这有助于您了解为什么a&a具有相同的地址以及(&a)[i]如何是i th 数组的地址(同一地址)大小与a的大小相同。

所以,声明

return (&a)[n] - a; 

相当于

return (&a)[n] - (&a)[0];  

这个差异将给出指针(&a)[n](&a)[0]之间的元素数量,这些元素是nn元素的int个数组。因此,总数组元素为n*n = n 2


注意:

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

  

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

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

另请注意,最好将函数p的返回类型更改为ptrdiff_t

答案 2 :(得分:35)

an 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

因此,

  1. (&a)[n]的类型是int[n]指针
  2. a的类型是int指针
  3. 现在表达式(&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
    
相关问题