数组是指针吗? [英] Are arrays Pointers?

查看:79
本文介绍了数组是指针吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

让我们先把重要的东西放在一边:数组不是指针.数组类型和指针类型是完全不同的东西,并且被编译器区别对待.

产生混淆的地方在于 C 如何处理数组表达式.N1570:

<块引用>

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

...
3 除非它是 sizeof 运算符、_Alignof 运算符或一元 & 运算符,或者是用于初始化数组的字符串文字,该表达式具有type ‘‘array of type’’被转换为一个类型为‘pointer to type’’的表达式,它指向到数组对象的初始元素并且不是左值.如果数组对象有注册存储类,行为未定义.

让我们看看以下声明:

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 位地址):

<前>对象地址 0x00 0x01 0x02 0x03------ ------- --------------arr 0x10008000 0x00 0x00 0x00 0x010x10008004 0x00 0x02 0x00 0x030x10008008 0x00 0x04 0x00 0x050x1000800c 0x00 0x06 0x00 0x070x10008010 0x00 0x08 0x00 0x09帕尔 0x10008014 0x10 0x00 0x80 0x00

类型对于 sizeof& 之类的东西很重要;sizeof arr == 10 * sizeof (int),在本例中为 20,而 sizeof parr == sizeof (int *),在本例中为 4.类似地,表达式&arr的类型是int (*)[10],或者是一个指向int,而 &parr 的类型是 int **,或指向 int 的指针的指针.

注意表达式 arr&arr 将产生相同的 value(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("之前: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);

before"行应该为所有三个表达式打印相同的值(在我们的假设映射中,0x10008000).after"行应显示三个不同的值:0x100080000x10008002(base plus sizeof (int))和 0x10008014(基础加上 sizeof (int [10])).

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

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

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

int foo(int *p, size_t size){整数总和 = 0;国际我;for (i = 0; i < size; i++){总和 += p[i];}返还金额;}int main(void){int arr[10] = {0,1,2,3,4,5,6,7,8,9};int 结果 = foo(arr, sizeof arr/sizeof arr[0]);...}

按照它的方式工作.main 处理一个 int 数组,而 foo 处理一个指向 int 的指针,但两者都是能够使用下标运算符就好像他们都在处理数组类型.

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

Are arrays and pointers implemented differently in C and C++? I have come across this question because, in both the cases we access elements from the starting address of an element. So, there should be close relation between them. Please explain the exact relation between them. Thanks.

解决方案

Let's get the important stuff out of the way first: arrays are not pointers. Array types and pointer types are completely different things and are treated differently by the compiler.

Where the confusion arises is from how C treats array expressions. 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.

Let's look at the following declarations:

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

arr is a 10-element array of int; it refers to a contiguous block of memory large enough to store 10 int values. The expression arr in the second declaration is of array type, but since it is not the operand of & or sizeof and it isn't a string literal, the type of the expression becomes "pointer to int", and the value is the address of the first element, or &arr[0].

parr is a pointer to int; it refers to a block of memory large enough to hold the address of a single int object. It is initialized to point to the first element in arr as explained above.

Here's a hypothetical memory map showing the relationship between the two (assuming 16-bit ints and 32-bit addresses):

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

The types matter for things like sizeof and &; sizeof arr == 10 * sizeof (int), which in this case is 20, whereas sizeof parr == sizeof (int *), which in this case is 4. Similarly, the type of the expression &arr is int (*)[10], or a pointer to a 10-element array of int, whereas the type of &parr is int **, or pointer to pointer to int.

Note that the expressions arr and &arr will yield the same value (the address of the first element in arr), but the types of the expressions are different (int * and int (*)[10], respectively). This makes a difference when using pointer arithmetic. For example, given:

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

the "before" line should print the same values for all three expressions (in our hypothetical map, 0x10008000). The "after" line should show three different values: 0x10008000, 0x10008002 (base plus sizeof (int)), and 0x10008014 (base plus sizeof (int [10])).

Now let's go back to the second paragraph above: array expressions are converted to pointer types in most circumstances. Let's look at the subscript expression arr[i]. Since the expression arr is not appearing as an operand of either sizeof or &, and since it is not a string literal being used to initialize another array, its type is converted from "10-element array of int" to "pointer to int", and the subscript operation is being applied to this pointer value. Indeed, when you look at the C language definition, you see the following language:

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

In practical terms, this means you can apply the subscript operator to a pointer object as though it were an array. This is why code like

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]);
  ...
}

works the way it does. main is dealing with an array of int, whereas foo is dealing with a pointer to int, yet both are able to use the subscript operator as though they were both dealing with an array type.

It also means array subscripting is commutative: assuming a is an array expression and i is an integer expression, a[i] and i[a] are both valid expressions, and both will yield the same value.

这篇关于数组是指针吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆