a,&a,* a,a ,&a 和&a 是否相同的指针?

我有以下的C程序:

#include <stdio.h> int main(){ int a[2][2] = {1, 2, 3, 4}; printf("a:%p, &a:%p, *a:%p \n", a, &a, *a); printf("a[0]:%p, &a[0]:%p \n", a[0], &a[0]); printf("&a[0][0]:%p \n", &a[0][0]); return 0; } 

它给出了以下输出:

 a:0028FEAC, &a:0028FEAC, *a:0028FEAC a[0]:0028FEAC, &a[0]:0028FEAC &a[0][0]:0028FEAC 

我无法理解为什么&aa*a都完全相同。 对于a[0]&a[0]&a[0][0]

编辑:

多亏了答案,我明白了为什么这些价值观是平等的。 Kernighan&Ritchie的书中的这一行certificate是我的问题的关键:

  the name of an array is a synonym for the location of the initial element. 

所以,通过这个,我们得到了

a = &a[0] ,和

a[0] = &a[0][0] (考虑作为数组的数组)

直觉上,现在原因很清楚。 但是,考虑如何在C中实现指针,我不明白a&a是如何相等的。 我假设在内存中有一个指向数组的variablesa (并且此数组的内存块的起始地址将是此variablesa的值)。

但是,当我们这样做&a ,这是不是意味着存储variablesa的内存地址呢? 为什么这些值是相等的呢?

他们不是相同的指针。 它们是指向不同types的指针,指向相同的内存位置。 相同的值(有点),不同的types。

C中的二维数组只不过是一个数组数组。

对象a的types是int[2][2] ,或者是2元素的int数组的2元素数组。

数组types的任何expression式在大多数但不是所有的上下文中都隐式转换为(“decay”)指向数组对象的第一个元素的指针。 因此, expression式 a除非是一元&sizeof的操作数,否则为int(*)[2] ,如果更清楚,则相当于&a[0] (或&(a[0]) )。 它变成了二维数组0行的指针。 重要的是要记住,这是一个指针 (或等价的地址 ),而不是一个指针对象 ; 这里没有指针对象,除非你明确地创build一个指针对象。

所以看看你问的几个expression式:

  • &a是整个数组对象的地址; 它是一个int(*)[2][2]types的指针expression式。
  • a是数组的名称。 正如上面所讨论的,它会“衰减”到指向数组对象的第一个元素(行)的指针。 这是一个int(*)[2]types的指针expression式。
  • *a指针expression式a 解除引用 由于a (在衰减之后)是一个2 int的数组的指针,所以*a是一个2 int s的数组。 因为这是一个数组types,所以它会衰减(在大多数情况下,但不是在所有情况下)到指向数组对象的第一个元素的指针。 所以它是int*types的。 *a相当于&a[0][0]
  • &a[0]是数组对象的第一行(第0行)的地址。 它的types是int(*)[2]a[0]是一个数组对象; 它不会衰减到指针,因为它是一元&的直接操作数。
  • &a[0][0]是数组对象的第0行的元素0的地址。 它是int*types的。

所有这些指针expression式都指向内存中的相同位置。 那个位置是数组对象a的开始; 它也是数组对象a[0]int对象a[0][0]

打印指针值的正确方法是使用"%p"格式并将指针值转换为void*

 printf("&a = %p\n", (void*)&a); printf("a = %p\n", (void*)a); printf("*a = %p\n", (void*)*a); /* and so forth */ 

这种转换为void*产生一个“原始”地址,该地址仅指定内存中的某个位置,而不指定该位置的对象types。 所以,如果你有多个不同types的指针指向在相同内存位置开始的对象,将它们全部转换为void*产生相同的值。

(我已经掩盖了[]索引操作符的内部工作,expression式x[y]的定义相当于*(x+y) ,其中x是一个指针(可能是数组隐式转换的结果), y是一个整数,反之亦然,但这很丑陋; arr[0]0[arr]是等价的,但是只有当你编写故意混淆的代码时才有用,如果我们考虑这个等价性,段落左右来描述a[0][0]意味着什么,这个答案可能已经太长了。)

为了完整起见,将数组types的expression式隐式转换为指向数组第一个元素的指针的三个上下文是:

  • 当它是一元操作数& ,所以&arr产生整个数组对象的地址;
  • 当它是sizeof的操作数时, sizeof arr产生数组对象的大小(以字节为单位),而不是指针的大小; 和
  • 当它是用于初始化一个数组(子)对象的初始化器中的string文字时,所以char s[6] = "hello"; 将数组值复制到s而不是用指针值非正确地初始化一个数组对象。 最后一个exception不适用于您所询问的代码。

(2011年ISO C标准的N1570草案错误地指出_Alignof是第四个exception;这是不正确的,因为_Alignof只能应用于括号内的types名称,而不是expression式,错误在最终的C11标准中得到纠正。 )

推荐阅读: comp.lang.c FAQ的第6部分。

因为所有expression式都指向数组的开头:

 a = {{a00},{a01},{a10},{a11}} 

指向数组,只是因为它是一个数组,所以a == &a[0]

&a[0][0]位于2Darrays的第一个单元格中。

它打印出相同的值,因为它们都指向相同的位置。

话说回来,

&a[i][i]的types是int * ,它是一个指向整数的指针。

a&a[0]的types为int(*)[2] ,表示指向2整数的指针。

&a的types是int(*)[2][2] ,表示指向2-D array的指针或指向两个元素的数组的指针,其中每个元素都是2个数组的数组。

所以,如果你开始对它们进行指针运算的话,它们都是不同types的,行为也不一样。

(&a[0][1] + 1)指向二维数组中的下一个整数元素,即指向a[0][1]

&a[0] + 1指向下一个整数数组,即指向a[1][0]

&a + 1指向在这种情况下不存在的下一个二维数组,但是如果存在则a[2][0]

  +------------------------------+ | a[0][0] <-- a[0] <-- a | // <--&a, a,*a, &a[0],&a[0][0] |_a[0][1]_ | | a[1][0] <-- a[1] | | a[1][1] | +------------------------------+ 

你知道a是数组的第一个元素的地址,根据C标准, a[X]等于*(a + X)

所以:

&a[0] == a因为&a[0]&(*(a + 0)) = &(*a) = a

&a[0][0] == a因为&a[0][0]&(*(*(a + 0) + 0))) = &(*a) = a

C中的二维数组被视为一维数组,其元素是一维数组(行)。
例如,一个T的4×3数组(其中“T”是某种数据types)可以用下面的公式表示: T a[4][3]

  +-----+-----+-----+ a == a[0] ---> | a00 | a01 | a02 | +-----+-----+-----+ +-----+-----+-----+ a[1] ---> | a10 | a11 | a12 | +-----+-----+-----+ +-----+-----+-----+ a[2] ---> | a20 | a21 | a22 | +-----+-----+-----+ +-----+-----+-----+ a[3] ---> | a30 | a31 | a32 | +-----+-----+-----+ 

数组元素也被存储在内存行中。
T加上[3] ,我们有一个3 T型元素的数组。 但是,名称a[4]本身就是一个数组,指示有4元素,每个元素都是3元素的数组。 因此,我们有一个由3元素组成的数组。
现在很清楚,指向a[4]的第一个元素( a[0] )。 另一方面&a[0]将给出a[4]的第一个元素( a[0] )的地址, &a[0][0]将给出数组的0th行的地址( a00 | a01 | a02) a[4][3]&a将给出二维数组a[3][4]*a指向a[0][0]指针衰减。
请注意, a不是指向a[0][0]的指针; 相反,它是一个指向a[0]的指针。
于是

  • G1: a&a[0]是等价的。
  • G2: *aa[0]&a[0][0]是等价的。
  • G3: &a (给出二维数组a[3][4] )。
    G1G2G3虽然给出了相同的结果 (我在上面解释了为什么给出了相同的结果), 但它们并不相同

这也意味着在C数组中没有开销。 在其他一些语言中,数组的结构是

 &a --> overhead more overhead &a[0] --> element 0 element 1 element 2 ... 

&a != &a[0]

直觉上,现在原因很清楚。 但是,考虑如何在C中实现指针,我不明白a和a是如何相等的。 我假设在内存中有一个指向数组的variablesa(并且此数组的内存块的起始地址将是此variablesa的值)。

那么,不。 存储器中任何地方都没有地址。 只有内存分配给原始数据,就是这样。 会发生什么呢,当你使用一个裸体a ,它立即变成一个指向第一个元素的指针,给人的印象是a的值是地址,但是a的唯一值是原始数组的存储。

事实上, a&a是不同的,但只是在types上,而不是在价值上。 通过使用一维数组来澄清这一点,让我们更容易一点:

 bool foo(int (*a)[2]) { //a function expecting a pointer to an array of two elements return (*a)[0] == (*a)[1]; //a pointer to an array needs to be dereferenced to access its elements } bool bar(int (*a)[3]); //a function expecting a pointer to an array of three elements bool baz(int *a) { //a function expecting a pointer to an integer, which is typically used to access arrays. return a[0] == a[1]; //this uses pointer arithmetic to access the elements } int z[2]; assert((size_t)z == (size_t)&z); //the value of both is the address of the first element. foo(&z); //This works, we pass a pointer to an array of two elements. //bar(&z); //Error, bar expects a pointer to an array of three elements. //baz(&z); //Error, baz expects a pointer to an int //foo(z); //Error, foo expects a pointer to an array //bar(z); //Error, bar expects a pointer to an array baz(z); //Ok, the name of an array easily decays into a pointer to its first element. 

正如你所看到的,即使它们具有相同的价值, a &a行为也会有所不同。