数组是指针?

可能重复:
数组名是C中的一个指针吗?

数组和指针的实现是不同的? 我遇到过这个问题,因为在这两种情况下,我们都是从一个元素的起始地址访问元素的,所以它们之间应该有密切的关系。 请解释他们之间的确切关系。 谢谢。

首先让我们看看重要的东西: 数组不是指针 。 数组types和指针types是完全不同的东西 ,并被编译器区别对待。

混淆的地方在于C如何处理数组expression式 。 N1570 :

6.3.2.1左值,数组和函数指示符


3除了是sizeof运算符的操作数, _Alignof运算符或一元运算符,或者是用于初始化数组的string文字,具有types为'''的数组的expression式被转换为expression式types“指向types”的指针指向数组对象的初始元素,而不是左值。 如果数组对象具有寄存器存储类,则行为是不确定的。

我们来看下面的声明:

 int arr[10] = {0,1,2,3,4,5,6,7,8,9}; int *parr = arr; 

arrint的10元素数组; 它指的是一个连续的内存块,足以存储10个int值。 第二个声明中的expression式 arr是数组types的,但是由于它不是一个string文字,因此它不是&sizeof的操作数, expression式的types变成“指向int指针”,并且该值是地址第一个元素,或者&arr[0]

parr是一个指向int的指针; 它指的是足够大的内存块来存放单个int对象的地址。 如上所述,它被初始化为指向arr的第一个元素。

下面是一个假设的内存映射,显示了两者之间的关系(假设是16位整数和32位地址):

对象地址0x00 0x01 0x02 0x03
 ------ ------- ----------------------
    arr 0x10008000 0x00 0x00 0x00 0x01
                  0x10008004 0x00 0x02 0x00 0x03
                  0x10008008 0x00 0x04 0x00 0x05
                  0x1000800c 0x00 0x06 0x00 0x07
                  0x10008010 0x00 0x08 0x00 0x09
   parr 0x10008014 0x10 0x00 0x80 0x00

这些types对于sizeof& sizeof arr == 10 * sizeof (int) ,在这种情况下是20,而sizeof parr == sizeof (int *) ,在本例中是4.同样,expression式&arr的types是int (*)[10]或者一个指向10个元素的int数组的指针,而&parr的types是int ** ,或者是指向int指针。

请注意,expression式arr&arr将产生相同的arr中第一个元素的地址),但是expression式的types不同( int *int (*)[10] )。 这在使用指针algorithm时有所不同。 例如,给出:

 int arr[10] = {0,1,2,3,4,5,6,7,8,9}; int *p = arr; int (*ap)[10] = &arr; printf("before: arr = %p, p = %p, ap = %p\n", (void *) arr, (void *) p, (void *) ap); p++; ap++; printf("after: arr = %p, p = %p, ap = %p\n", (void *) arr, (void *) p, (void *) ap); 

“之前”行应该打印所有三个expression式(在我们的假设的地图, 0x10008000 )相同的值。 “后”行应该显示三个不同的值: 0x10008000 (base plus sizeof (int) )和0x10008014 (base plus sizeof (int [10]) )。

现在让我们回到上面的第二段:在大多数情况下,数组expression式被转换为指针types。 我们来看看下标expression式arr[i] 。 由于expression式arr不是作为sizeof&的操作数出现的,并且由于它不是用于初始化另一个数组的string文字,所以它的types从“10元素数组的int ”转换为“指向int指针” ,并且下标操作正被应用于该指针值。 事实上,当您查看C语言定义时,您会看到以下语言:

6.5.2.1数组下标

2后缀expression式后面跟着方括号[]中的expression式是数组对象元素的下标。 下标运算符[]的定义是E1 [E2](*((E1)+(E2)))相同 。 由于适用于二元运算符的转换规则,如果E1是一个数组对象(相当于一个指向数组对象的初始元素的指针)并且E2是一个整数,则E1 [E2]表示E2的E2个元素E1 (从零开始计数)。

实际上,这意味着您可以将下标运算符应用于指针对象, 就像它是一个数组一样。 这就是为什么代码

 int foo(int *p, size_t size) { int sum = 0; int i; for (i = 0; i < size; i++) { sum += p[i]; } return sum; } int main(void) { int arr[10] = {0,1,2,3,4,5,6,7,8,9}; int result = foo(arr, sizeof arr / sizeof arr[0]); ... } 

按照它的方式工作。 main是处理一个int数组,而foo处理的是一个指向int的指针,但都能够使用下标运算符, 就像它们都处理数组types一样。

这也意味着数组下标是可交换的 :假设a是一个数组expression式,而i是一个整数expression式, a[i]i[a]都是有效的expression式,并且两者都会产生相同的值。

不知道C ++。 对于C来说, c-faq的回答比我以往任何时候都好。

c-faq的小片段:

6.3那么C中“指针和数组的等价性”是什么意思呢?

[…]

具体来说,等价的基石是这个关键的定义:

对expression式T中出现的types为T的对象的引用会衰减(有三个例外)为指向其第一个元素的指针; 结果指针的types是指向T的指针。

[…]

在C ++中根据C ++标准4.2:

可以将“T的数组”或“T的未知数组”的左值或右值转换为types“指向T的指针”的右值。结果是指向数组的第一个元素的指针。

不,他们没有以不同的方式实施 。 两者都find具有相同计算的元素: a[i]在地址a + i*sizeof(a[0]) ,而p[i]在地址p + i*sizeof(p[0])

但是, 他们被types系统区别对待 。 C ++已经通过sizeof operator (如C),模板推理,函数重载,RTTI等等来查看数组的信息。 基本上在使用types信息的语言中的任何地方,指针和数组的行为可能是不同的。

在C ++中有很多很多的例子,其中两种不同的语言概念具有相同的实现。 只有几个:数组vs指针,指针vs引用,虚函数vs函数指针,迭代器vs指针,循环vs while循环,exceptionvs longjmp

在任何情况下,对于这两个概念都有不同的语法和思维方式,但最终会得到相同的机器代码。

在C + +(我也想C),一个数组不是一个指针,可以通过以下方式certificate。

 #include <iostream> int main() { char arr[1000]; std::cout << sizeof arr; } 

如果arr是一个指针,这个程序会打印sizeof(char *),它通常是4.但是它打印1000。

另一个certificate:

 template <class T> void f(T& obj) { T x = obj; //this will fail to compile if T is an array type } int main() { int a[30] = {}; int* p = 0; f(p); //OK f(a); //results in compile error. Remember f takes by ref therefore needs lvalue and no conversion applies } 

forms上,一个数组被转换为一个指向它的第一个元素的左值到右值的转换,也就是当一个数组types的左值在上下文中被给定,当一个右值被期望的时候,该数组被转换为指向它的第一个元件。

此外,声明按值取数组的函数与取指针函数相同,也就是说

 void f(int a[]); void f(int a[10]); void f(int* a); 

是三个相同的声明。 HTH

在C ++数组types中有一个“大小属性”,所以为

 T a[10]; T b[20]; 

ab有不同的types。

这允许使用这样的代码

 template<typename T, size_t N> void foo(T (&a)[N]) { ... } 

数组和指针之间混淆的最大问题来自于K&R决定将函数参数声明为数组types,就好像它们被声明为指针一样。 声明

  void foo(int a []); 

  void foo(int * a); 

是等同的,就像(据我所知)

  void foo(int a [5]); 

尽pipe我并不积极,但编译器在后一个函数中需要接受对[6]的引用。 在其他上下文中,数组声明为指定数量的元素分配空间。 请注意,给定:

 typedef int foo [1];

footypes的任何声明都会为一个元素分配空间,但是任何将foo作为函数parameter passing的尝试都会传递该地址。 在学习一个va_list实现时学到了一些有用的技巧。