数组是指针? [重复]

问题描述 投票:16回答:7

可能重复: Is array name a pointer in C?

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

c++ c arrays pointers
7个回答
52
投票

让我们首先得到重要的东西:数组不是指针。数组类型和指针类型是完全不同的东西,编译器会对它们进行不同的处理。

出现混淆的地方是C如何处理数组表达式。 N1570

6.3.2.1 Lvalues, arrays, and function designators

...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

我们来看看以下声明:

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

arr是一个10元素的int阵列;它指的是一个连续的内存块,足以存储10个int值。第二个声明中的表达式arr是数组类型,但由于它不是&sizeof的操作数而且它不是字符串文字,表达式的类型变为“指向int的指针”,值为地址第一个元素,或&arr[0]

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

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

Object           Address         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

这些类型对于像sizeof&这样的东西很重要; sizeof arr == 10 * sizeof (int),在这种情况下是20,而sizeof parr == sizeof (int *),在这种情况下是4.同样,表达式&arr的类型是int (*)[10],或指向int的10元素阵列,而&parr的类型是int **,或指向int指针的指针。

请注意,表达式arr&arr将产生相同的值(arr中第一个元素的地址),但表达式的类型不同(分别为int *int (*)[10])。这在使用指针运算时有所不同。例如,给定:

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);

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

现在让我们回到上面的第二段:在大多数情况下,数组表达式转换为指针类型。让我们看一下下标表达式arr[i]。由于表达式arr没有出现为sizeof&的操作数,并且因为它不是用于初始化另一个数组的字符串文字,所以它的类型从“10个元素的int数组”转换为“指向int的指针” ,并将下标操作应用于此指针值。实际上,当您查看C语言定义时,您会看到以下语言:

6.5.2.1 Array subscripting
...
2 A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

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

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的指针,但两者都能够使用下标运算符,就好像它们都处理数组类型一样。

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


19
投票

不了解C ++。对于C来说,c-faq的回答比以往任何时候都要好得多。

来自c-faq的小片段:

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

[...]

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

对表达式中出现的array-of-T类型的对象的引用将(有三个例外)衰减为指向其第一个元素的指针;结果指针的类型是指向T的指针。

[...]


6
投票

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

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


6
投票

不,它们的实现方式不同。两者都找到具有相同计算的元素:a[i]位于地址a + i*sizeof(a[0])p[i]位于地址p + i*sizeof(p[0])

但是,类型系统对它们的处理方式不同。 C ++在数组上有输入信息,可以通过sizeof operator(如C),模板推理,函数重载,RTTI等查看。基本上语言中使用类型信息的任何地方,指针和数组都可能表现不同。

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

在每种情况下,都有不同的语法和不同的思考方式,但最终会产生相同的机器代码。


5
投票

在C ++中(我认为在C中),数组不是指针,可以通过以下方式证明。

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

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

另一个证据:

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
}

形式上,一个数组被转换为指向lvalue-to-rvalue转换中第一个元素的指针,也就是说,当一个rvalue被预期时,在一个上下文中给出一个数组类型的左值,该数组被转换为指向它的第一个元件。

此外,声明为按值获取数组的函数等效于获取指针的函数,即

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

是三个等同的声明。 HTH


1
投票

数组和指针之间最大的混淆点来自于K&R决定将声明为数组类型的函数参数表现为就像它们被声明为指针一样。声明

void foo(int a[]);
and
void foo(int *a);
are equivalent, as is (so far as I can tell)
void foo(int a[5]);
though I'm not positive a compiler would be required to accept a reference to a[6] within the latter function. In other contexts, an array declaration allocates space for the indicated number of elements. Note that given:
typedef int foo[1];

类型foo的任何声明都将为一个元素分配空间,但任何将foo作为函数参数传递的尝试都将传递该地址。我在研究va_list实现时学到了一些有用的技巧。


1
投票

在C ++中,数组类型有一个“大小属性”,所以对于

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

a和b有不同的类型。

这允许使用这样的代码

template<typename T, size_t N>
void foo(T (&a)[N])
{
   ...
}
© www.soinside.com 2019 - 2024. All rights reserved.