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 
 我无法理解为什么&a , a , *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(在衰减之后)是一个2int的数组的指针,所以*a是一个2ints的数组。 因为这是一个数组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:
*a,a[0]和&a[0][0]是等价的。- G3:
&a(给出二维数组a[3][4])。
但G1组, G2组和G3组虽然给出了相同的结果 (我在上面解释了为什么给出了相同的结果), 但它们并不相同 。
这也意味着在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行为也会有所不同。