c中的数组名称究竟是什么?

时间:2014-06-28 14:19:52

标签: c arrays pointers memory-address

我很难理解C中数组名称的类型和用法。这可能看起来很长,但请耐心等待。

我理解以下声明声明aint []类型,即整数数组

int a[30];

虽然a也指向数组的第一个元素,但*(a+2)之类的内容有效。因此,使a看起来像指向整数的指针。但实际上int []int*类型不同;前者是数组类型,后来是指向整数的指针。

同样,int []类型的变量在将其传递给函数时会转换为类型int*的变量;在C数组中,通过引用传递(sizeof运算符除外)。

这就是让我垂涎的观点。看看下面这段代码:

int main()
{
    int (*p)[3];
    int a[3] = { 5, 4, 6 };

    p = &a;

    printf("a:%d\t&a:%d\n",a,&a);
    printf("%d",*(*p + 2));
}

输出:

a:2686720       &a:2686720
6

那么,上面的代码是如何工作的呢?我有两个问题:

  1. a&a具有相同的值。为什么?
  2. int (*p)[3];究竟做了什么?它声明了一个指向数组的指针,我知道这一点。但是指向数组的指针与指向数组第一个元素的指针数组的名称有什么不同?
  3. 任何人都可以澄清一切吗?我有很多困惑。

    我知道我应该使用%p作为占位符,而不是使用%d来打印指针变量的值。使用整数占位符可能会打印截断的地址。但我只想保持简单。

6 个答案:

答案 0 :(得分:47)

其他答案已经解释了这个问题。我试图用一些图解释它。希望这会有所帮助。


声明数组时

int a[3] = {5, 4, 6}  

内存安排看起来像

enter image description here

现在回答你的问题:

  
      
  1. a&a具有相同的价值。如何?
  2.   

正如您已经知道a是数组类型而数组名a成为指向数组a的第一个元素的指针(在衰减之后),即它指向地址{ {1}}。请注意,0x100也是内存块的起始地址(数组0x100)。你应该知道,通常,第一个字节的地址被称为变量的地址。也就是说,如果变量是100个字节,那么它的地址等于其第一个字节的地址。

a是整个内存块的地址,即它是数组&a的地址。见图:

enter image description here

现在您可以理解为什么aa都具有相同的地址值,尽管两者的类型不同。

  

它到底是做什么&a声明一个指向数组的指针,我知道这个。但是,指向数组的指针与指向数组的第一个元素的指针和数组的名称有何不同?

参见上图,清楚地解释了指向数组的指针与指向数组元素的指针的不同之处 当您将int (*p)[3];分配给&a时,p会指向具有起始地址p的整个数组。


注意:关于

  

...在0x100数组中通过引用传递(C函数除外)。

在C中,参数按值传递。 C中没有通过引用传递。当普通变量传递给函数时,其值为复制;对相应参数的任何更改都不会影响变量 数组也通过值传递,但区别在于数组名称衰减到指向第一个元素的指针,并且该指针分配给函数的参数(这里,指针值被复制);数组本身不会被复制 与普通变量相比,用作参数的数组不受任何保护,因为没有复制数组本身,而是指向第一个元素的指针复制。

您还应该注意sizeof不是函数,在这种情况下数组名称不作为参数。 sizeof运算符,数组名称用作操作数。当数组名是一元sizeof运算符的操作数时,也是如此。

答案 1 :(得分:16)

  
      
  1. a和& a具有相同的值。怎么样?
  2.   

它们具有相同的值但不同的类型。数组对象在元素之间(之前或之后)没有填充,因此数组的地址和数组的第一个元素的地址是相同的。

那是:

(void *) a == (void *) &a
  
      
  1. 它到底是做什么的(* p)[3];声明一个指向数组的指针,我知道这个。但是,指向数组的指针与指向数组的第一个元素的指针和数组名称有何不同?
  2.   

这是两种不同的指针类型。例如,指针算术:

a + 1   /* address of the second element of the array */
&a + 1  /* address one past the last element of the array */

编辑:由于受欢迎的需求,我在下面添加了一些有关数组转换的信息。

除了三个例外,在表达式中,类型为T的数组的对象将转换为指向数组的第一个元素的T类型指针的值。例外情况是对象是sizeof&一元运算符的操作数,或者对象是初始化数组的字符串文字。

例如这句话:

printf("a:%d\t&a:%d\n", a, &a);

实际上相当于:

printf("a:%d\t&a:%d\n", &a[0], &a);

另请注意,d转换说明符只能用于打印有符号整数;要打印指针值,必须使用p说明符(参数必须为void *)。所以要正确使用:

printf("a:%p\t&a:%p\n", (void *) a, (void *) &a);

分别为:

printf("a:%p\t&a:%p\n", (void *) &a[0], (void *) &a);

答案 2 :(得分:2)

  1. a对应于指向数组的第0个元素的指针。然而,& a的情况也是如此。它只是给出了数组的起始地址。
  2. 如,a --> pointer pointing to starting element of array a[],it does not know about other element's location.

    &a --->address location for storing array a[] which stores first element location,but knows every element's location

    同样,其他元素的位置将是(a + 2),(a + 4),因此直到数组的末尾。

    因此,你得到了这样的结果。

    1. int(* p)[3]是指向数组的指针。如果它是int * p [3],它将意味着完全不同。它意味着一系列指针与这种背景完全不同。
    2.   

      指向数组的指针将自动处理所有其他指针   数组中的元素。在这种情况下,你的是(p);

           

      然而,指向数组第一个元素的指针,即一个意志   只知道数组的第一个元素。你必须手动   给出指针算术方向以访问下一个元素。参见,在此   case ---我们可以通过将2添加到a来获得第二个元素,即。   a + 2,第三个元素是将a加4,即a + 4,依此类推。 //记住   差异为2,因为它是一个整数数组!

答案 3 :(得分:1)

在回答问题1时,这只是设计的C语言的一个方面,与大多数其他现代语言不同,C / C ++允许直接操作内存中的地址,并具有内置的设施以便理解'那。网上有很多文章比这个小空间更能解释这一点。这是一个,我相信还有很多其他的:http://www.cprogramming.com/tutorial/c/lesson8.html

答案 4 :(得分:1)

来自C99 Standard n1124 6.3.2.1 p3

  

除非它是sizeof运算符或一元&的操作数。   operator,或者是用于初始化数组的字符串文字,a   具有类型''数组类型''的表达式将转换为   带有''指向类型'的指针的表达式,指向初始值   数组对象的元素,而不是左值。如果是数组对象   有寄存器存储类,行为未定。

答案 5 :(得分:0)

a和& a具有相同的值,因为很久以前你被要求使用地址运算符&在数组上获取数组的地址,但不再需要。这些天的数组名称(在本例中为a)只代表数组本身的内存地址,这也是你从& a获得的内容。它是编译器为您处理的简写。