C++|表达式中的数据名为什么会自动转换为指向数组首元素的指针

表达式中的数据名为什么会自动转换为指向数组首元素的指针?一言以蔽之,操作数组的重点在于操作数组的每一个元素,而不是操作其整体。怎样操作数组元素呢?地址偏移,也就是指针的算术运算,一些指针算术运算(如++、--,+n、-n)只对数组元素有意义,对数组整体意义不大。所以表达式中的数据名需要自动转换为指向数组首元素的指针,用数组名来充当指向首地址的偏移基准,为安全起见(因为是基准,如果基准修改了,其偏移就会乱套),让数组名具有常量性质(这也是数组不支持整体赋值的原因)。另外,数组的下标操作也只是针对元素指针算术运算的语法糖。

Pointers and arrays are closely intertwined in C++. In particular, when we use the name of an array in an expression, that name is automatically converted into a pointer to the first element of the array:

C++ 语言中,指针和数组密切相关。特别是在表达式中使用数组名时,该名字会自动转换为指向数组第一个元素的指针:

 int ia[] = {0,2,4,6,8};
int *ip = ia; // ip points to ia[0]
cout << *(ia+2) << endl;
int *arrp = &ia; // arrp points to the whole ia
int *p = arrp+1; //p pints to the next object of ia

The exceptions when an array is not converted to a pointer are: as the operand of the address-of (&) operator or of sizeof, or when using the array to initialize a reference to the array. We'll see how to define a reference (or pointer) to an array in Section 7.2.4 (p. 240).

不将数组转换为指针的例外情况有:数组用作取地址(&)操作符的操作数或 sizeof 操作符的操作数时,或用数组对数组的引用进行初始化时,不会将数组转换为指针。

If we want to point to another element in the array, we could do so by using the subscript operator to locate the element and then applying the address-of operator to find its location:

如果希望使指针指向数组中的另一个元素,则可使用下标操作符给某个元素定位,然后用取地址操作符 & 获取该元素的存储地址:

 ip = &ia[4];    // ip points to last element in ia

When we subscript an array, we are really subscripting a pointer:

使用下标访问数组时,实际上是使用下标访问指针:

 int ia[] = {0,2,4,6,8};
 int i = ia[0]; // ia points to the first element in ia

Array names are nonmodifiable lvalues: An array cannot be the target of an assignment. Both the subscript and dereference operators return lvalues. The result of dereference or subscript, when applied to a nonconst array, can be the left-hand operand of an assignment:

数组名是不可修改的左值:因此数组不可用作赋值操作的目标。而下标和解引用操作符都返回左值,因此当将这两种操作用于非 const 数组时,其结果可作为赋值操作的左操作数:

     int ia[10];
     ia[0] = 0;    // ok: subscript is an lvalue
     *ia = 0;      // ok: dereference also is an lvalue

1 指向多维数组首元素的指针

As with any array, when we use the name of a multidimensioned array, it is automatically converted to a pointer to the first element in the array.

与普通数组一样,使用多维数组名时,实际上将其自动转换为指向该数组第一个元素的指针。

When defining a pointer to a multidimensioned array, it is essential to remember that what we refer to as a multidimensioned array is really an array of arrays.

定义指向多维数组的指针时,千万别忘了该指针所指向的多维数组其实是数组的数组。

Because a multidimensioned array is really an array of arrays, the pointer type to which the array converts is a pointer to the first inner array. Although conceptually straightforward, the syntax for declaring such a pointer can be confusing:

因为多维数组其实就是数组的数组,所以由多维数组转换而成的指针类型应是指向第一个内层数组的指针。尽管这个概念非常明了,但声明这种指针的语法还是不容易理解:

int ia[3][4];      // array of size 3, each element is an array of ints of size 4
int (*ip)[4] = ia; // ip points to an array of 4 ints
ip = &ia[2];       // ia[2] is an array of 4 ints

We define a pointer to an array similarly to how we would define the array itself: We start by declaring the element type followed by a name and a dimension. The trick is that the name is a pointer, so we must prepend * to the name. We can read the definition of ip from the inside out as saying that *ip has type int[4] that is, ip is a pointer to an int array of four elements.

定义指向数组的指针与如何定义数组本身类似:首先声明元素类型,后接(数组)变量名字和维数。窍门在于(数组)变量的名字其实是指针,因此需在标识符前加上 *。如果从内向外阅读 ip 的声明,则可理解为:*ip 是 int[4] 类型——即 ip 是一个指向含有 4 个元素的数组的指针。

Typedefs can help make pointers to elements in multidimensioned arrays easier to write, read, and understand. We might write a typedef for the element type of ia as

typedef 类型定义可使指向多维数组元素的指针更容易读、写和理解。以下程序用 typedef 为 ia 的元素类型定义新的类型名:

typedef int int_array[4];
int_array *ip = ia;

We might use this typedef to print the elements of ia:

可使用 typedef 类型输出 ia 的元素:

     for (int_array *p = ia; p != ia + 3; ++p)
         for (int *q = *p; q != *p + 4; ++q)
              cout << *q << endl;

2 数组和指针 VS vector 类、string 类和相关的迭代器

Arrays and pointers provide functionality similar to that provided by the library vector and string types and their companion iterators. The vector type can be thought of as a more flexible, easier to manage array. Similarly, strings are a great improvement on C-style strings that are implemented as null-terminated character arrays.

数组和指针所提供的功能类似于标准库的 vector 类与 string 类和相关的迭代器所提供的功能。我们可以把 vector 类型理解为更灵活、更容易管理的数组,同样,string 是 C 风格字符串的改进类型,而 C 风格字符串是以空字符结束的字符数组。

编程需要逐步提高数据的颗粒度或抽象度,相对于数组和指针,vector类和迭代器是更高层面的抽象,具有更大的数据颗粒度。

迭代器的实质是容器元素指针的封装,同时封装了类似于指针算术运算的操作符重载函数。

The library defines an iterator type for each of the standard containers, including vector. Iterators are more general than subscripts: All of the library containers define iterator types, but only a few of them support subscripting. Because iterators are common to all containers, modern C++ programs tend to use iterators rather than subscripts to access container elements, even on types such as vector that support subscripting.

标准库为每一种标准容器(包括 vector)定义了一种迭代器类型。迭代器类型提供了比下标操作更通用化的方法:所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。因为迭代器对所有的容器都适用,现代 C++ 程序更倾向于使用迭代器而不是下标操作访问容器元素,即使对支持下标操作的 vector 类型也是这样。

2.1 使用数组初始化 vector 对象

We noted that it is not possible to initialize an array from another array. Instead, we have to create the array and then explicitly copy the elements from one array into the other. It turns out that we can use an array to initialize a vector, although the form of the initialization may seem strange at first. To initialize a vector from an array, we specify the address of the first element and one past the last element that we wish to use as initializers:

不能用一个数组直接初始化另一数组,程序员只能创建新数组,然后显式地把源数组的元素逐个复制给新数组。这反映 C++ 允许使用数组初始化 vector 对象,尽管这种初始化形式起初看起来有点陌生。使用数组初始化 vector 对象,必须指出用于初始化式的第一个元素以及数组最后一个元素的下一位置的地址:

          const size_t arr_size = 6;
          int int_arr[arr_size] = {0, 1, 2, 3, 4, 5};
          // ivec has 6 elements: each a copy of the corresponding element in int_arr
          vector ivec(int_arr, int_arr + arr_size);

2.2 尽量避免使用指针和数组

Pointers and arrays are surprisingly error-prone. Part of the problem is conceptual: Pointers are used for low-level manipulations and it is easy to make bookkeeping mistakes. Other problems arise because of the syntax, particularly the declaration syntax used with pointers.

指针和数组容易产生不可预料的错误。其中一部分是概念上的问题:指针用于低级操作,容易产生与繁琐细节相关的(bookkeeping)错误。其他错误则源于使用指针的语法规则,特别是声明指针的语法。

Many useful programs can be written without needing to use arrays or pointers. Instead, modern C++ programs should use vectors and iterators to replace general arrays and strings to replace C-style array-based character strings.

许多有用的程序都可不使用数组或指针实现,现代C++程序采用vector类型和迭代器取代一般的数组、采用string类型取代C风格字符串。

Pointers and arrays can be necessary for certain low-level tasks, but they should be avoided because they are error-prone and hard to debug. In general, the library abstractions should be used in preference to low-level array and pointer alternatives built into the language. This advice is especially applicable to using strings instead of C-style null-terminated character arrays. Modern C++ programs should not use C-style strings.

某些低级任务必须使用指针和数组,但由于使用指针和数组容易出错而且难以调试,应尽量避免使用。一般而言,应该优先使用标准库抽象类而少用语言内置的低级数组和指针。尤其是应该使用 string 类型取代 C 风格以空字符结束的字符数组。现代 C++ 程序不应使用C风格字符串。

3 数组形参

Arrays have two special properties that affect how we define and use functions that operate on arrays: We cannot copy an array and when we use the name of an array it is automatically converted to a pointer to the first element. Because we cannot copy an array, we cannot write a function that takes an array type parameter. Because arrays are automatically converted to pointers, functions that deal with arrays usually do so indirectly by manipulating pointers to elements in the array.

数组有两个特殊的性质,影响我们定义和使用作用在数组上的函数:一是不能复制数组;二是使用数组名字时,数组名会自动转化为指向其第一个元素的指针。因为数组不能复制,所以无法编写使用数组类型形参的函数。因为数组会被自动转化为指针,所以处理数组的函数通常通过操纵指向数组指向数组中的元素的指针来处理数组。

Even though we cannot pass an array directly, we can write a function parameter that looks like an array. Despite appearances, a parameter that uses array syntax is treated as if we had written a pointer to the array element type.

虽然不能直接传递数组,但是函数的形参可以写成数组的形式。虽然形参表示方式不同,但可将使用数组语法定义的形参看作指向数组元素类型的指针。

Rather than using the array syntax. Doing so makes it clear that what is being operated on is a pointer to an array element, not the array itself. Because an array dimension is ignored, including a dimension in a parameter definition is particularly misleading.

通常,将数组形参直接定义为指针要比使用数组语法定义更好。这样就明确地表示,函数操纵的是指向数组元素的指针,而不是数组本身。由于忽略了数组长度,形参定义中如果包含了数组长度则特别容易引起误解。

As with any array, a multidimensioned array is passed as a pointer to its zeroth element. An element in a multidimenioned array is an array. The size of the second (and any subsequent dimensions) is part of the element type and must be specified:

和其他数组一样,多维数组以指向 0 号元素的指针方式传递。多维数组的元素本身就是数组。除了第一维以外的所有维的长度都是元素类型的一部分,必须明确指定:

     // first parameter is an array whose elements are arrays of 10 ints
     void printValues(int (matrix*)[10], int rowSize);

declares matrix as a pointer to an array of ten ints.

上面的语句将 matrix 声明为指向含有 10 个 int 型元素的数组的指针。

We could also declare a multidimensioned array using array syntax. As with a single-dimensioned array, the compiler ignores the first dimension and so it is best not to include it:

我们也可以用数组语法定义多维数组。与一维数组一样,编译器忽略第一维的长度,所以最好不要把它包括在形参表内:

     // first parameter is an array whose elements are arrays of 10 ints
     void printValues(int matrix[][10], int rowSize);

4 术语

pointer(指针)

An object that holds the address of an object.

存放对象地址的对象。

pointer arithmetic(指针算术操作)

The arithmetic operations that can be applied to pointers. An integral type can be added to or subtracted from a pointer, resulting in a pointer positioned that many elements ahead or behind the original pointer. Two pointers can be subtracted, yielding the difference between the pointers. Pointer arithmetic is valid only on pointers that denote elements in the same array or an element one past the end of that array.

可用于指针的算术操作。允许在指针上做加上或减去整型值的操作,以获得当前指针之前或之后若干个元素处的地址。两个指针可做减法操作,得到它们之间的差值。只有当指针指向同一个数组或其超出末端的位置时,指针的算术操作才有意义。

* operator(* 操作符)

Dereferencing a pointer yields the object to which the pointer points. The dereference operator returns an lvalue; we may assign to the value returned from the dereference operator, which has the effect of assigning a new value to the underlying element.

对指针进行解引用操作获得该指针所指向的对象。解引用操作符返回左值,因此可为其结果赋值,等效于为该指针所指向的特定对象赋新值。

++ operator(++ 操作符)

When used with a pointer, the increment operator "adds one" by moving the pointer to refer to the next element in an array.

用于指针时,自增操作符给指针“加 1”,移动指针使其指向数组的下一个元素。

[] operator([] 操作符)

The subscript operator takes two operands: a pointer to an element of an array and an index. Its result is the element that is offset from the pointer by the index. Indices count from zerothe first element in an array is element 0, and the last is element size of the array minus 1. The subscript operator returns an lvalue; we may use a subscript as the left-hand operand of an assignment, which has the effect of assigning a new value to the indexed element.

下标操作符接受两个操作数:一个是指向数组元素的指针,一个是下标 n。该操作返回偏离指针当前指向 n 个位置的元素值。数组下标从 0 开始计数——数组第一个元素的下标为 0,最后一个元素的下标是数组长度减 1。下标操作返回左值,可用做赋值操作的左操作数,等效于为该下标引用的元素赋新值。

& operator(& 操作符)

The address-of operator. Takes a single argument that must be an lvalue. Yields the address in memory of that object.

取地址操作符需要一个操作数,其唯一的操作数必须是左值对象,该操作返回操作数对象在内存中的存储地址。

void*

A pointer type that can point to any nonconst type. Only limited operations are permitted on void* pointers. They can be passed or returned from functions and they can be compared with other pointers. They may not be dereferenced.

可以指向任何非 const 对象的指针类型。void* 指针只提供有限的几种操作:可用作函数形参类型或返回类型,也可与其他指针做比较操作,但是不能进行解引用操作。

ref:

C++ primer / Stanley B. Lippman, Josée Lajoie, Barbara E. Moo

-End-

展开阅读全文

页面更新:2024-05-27

标签:数组   指针   多维   元素   下标   赋值   算术   表达式   初始化   语法   函数   定义   对象   类型   操作

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top