Team LiB
Previous Section Next Section

4.2. Introducing Pointers

4.2. 指针的引入

Just as we can traverse a vector either by using a subscript or an iterator, we can also traverse an array by using either a subscript or a pointer. A pointer is a compound type; a pointer points to an object of some other type. Pointers are iterators for arrays: A pointer can point to an element in an array. The dereference and increment operators, when applied to a pointer that points to an array element, have similar behavior as when applied to an iterator. When we dereference a pointer, we obtain the object to which the pointer points. When we increment a pointer, we advance the pointer to denote the next element in the array. Before we write programs using pointers, we need to know a bit more about them.

vector 的遍历可使用下标或迭代器实现,同理,也可用下标或指针来遍历数组。指针是指向某种类型对象的复合数据类型,是用于数组的迭代器:指向数组中的一个元素。在指向数组元素的指针上使用解引用操作符 *(dereference operator)和自增操作符 ++(increment operator),与在迭代器上的用法类似。对指针进行解引用操作,可获得该指针所指对象的值。而当指针做自增操作时,则移动指针使其指向数组中的下一个元素。在使用指针编写程序之前,我们需进一步了解一下指针。

4.2.1. What Is a Pointer?

4.2.1. 什么是指针

For newcomers, pointers are often hard to understand. Debugging problems due to pointer errors bedevil even experienced programmers. However, pointers are an important part of most C programs and to a much lesser extent remain important in many C++ programs.

对初学者来说,指针通常比较难理解。而由指针错误引起的调试问题连富有经验的程序员都感到头疼。然而,指针是大多数C程序的重要部分,而且在许多 C++ 程序中仍然受到重用。

Conceptually, pointers are simple: A pointer points at an object. Like an iterator, a pointer offers indirect access to the object to which it points. However, pointers are a much more general construct. Unlike iterators, pointers can be used to point at single objects. Iterators are used only to access elements in a container.

指针的概念很简单:指针用于指向对象。与迭代器一样,指针提供对其所指对象的间接访问,只是指针结构更通用一些。与迭代器不同的是,指针用于指向单个对象,而迭代器只能用于访问容器内的元素。

Specifically, a pointer holds the address of another object:

具体来说,指针保存的是另一个对象的地址:

          string s("hello world");
          string *sp = &s; // sp holds the address of s

The second statement defines sp as a pointer to string and initializes sp to point to the string object named s. The * in *sp indicates that sp is a pointer. The & operator in &s is the address-of operator. It returns a value that when dereferenced yields the original object. The address-of operator may be applied only to an lvalue (Section 2.3.1, p. 45). Because a variable is an lvalue, we may take its address. Similarly, the subscript and dereference operators, when applied to a vector, string, or built-in array, yield lvalues. Because these operators yield lvalues, we may apply the address-of to the result of the subscript or dereference operator. Doing so gives us the address of a particular element.

第二条语句定义了一个指向 string 类型的指针 sp,并初始化 sp 使其指向 string 类型的对象s。*sp 中的 * 操作符表明 sp 是一个指针变量,&s 中的 & 符号是取地址操作符,当此操作符用于一个对象上时,返回的是该对象的存储地址。取地址操作符只能用于左值(第 2.3.1 节),因为只有当变量用作左值时,才能取其地址。同样地,由于用于 vector 类型、string 类型或内置数组的下标操作和解引用操作生成左值,因此可对这两种操作的结果做取地址操作,这样即可获取某一特定对象的存储地址。

Advice: Avoid Pointers and Arrays

建议:尽量避免使用指针和数组

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风格字符串。


4.2.2. Defining and Initializing Pointers

4.2.2. 指针的定义和初始化

Every pointer has an associated type. The type of a pointer determines the type of the objects to which the pointer may point. A pointer to int, for example, may only point to an object of type int.

每个指针都有一个与之关联的数据类型,该数据类型决定了指针所指向的对象的类型。例如,一个 int 型指针只能指向 int 型对象。

Defining Pointer Variables
指针变量的定义

We use the * symbol in a declaration to indicate that an identifier is a pointer:

C++ 语言使用 * 符号把一个标识符声明为指针:

          vector<int>   *pvec;      // pvec can point to a vector<int>
          int           *ip1, *ip2; // ip1 and ip2 can point to an int
          string        *pstring;   // pstring can point to a string
          double        *dp;        // dp can point to a double

When attempting to understand pointer declarations, read them from right to left.

理解指针声明语句时,请从右向左阅读。



Reading the definition of pstring from right to left, we see that

从右向左阅读 pstring 变量的定义,可以看到

          string *pstring;

defines pstring as a pointer that can point to string objects. Similarly,

语句把 pstring 定义为一个指向 string 类型对象的指针变量。类似地,语句

          int *ip1, *ip2; // ip1 and ip2 can point to an int

defines ip2 as a pointer and ip1 as a pointer. Both pointers point to ints.

ip1ip2 都定义为指向 int 型对象的指针。

The * can come anywhere in a list of objects of a given type:

在声明语句中,符号 * 可用在指定类型的对象列表的任何位置:

          double dp, *dp2; // dp2 is a ponter, dp is an object: both type double

defines dp2 as a pointer and dp as an object, both of type double.

该语句定义了一个 double 类型的 dp 对象以及一个指向 double 类型对象的指针dp2。

A Different Pointer Declaration Style
另一种声明指针的风格

The * symbol may be separated from its identifier by a space. It is legal to write:

在定义指针变量时,可用空格将符号 * 与其后的标识符分隔开来。下面的写法是合法的:

          string* ps; // legal but can be misleading

which says that ps is a pointer to string.

也就是说,该语句把 ps 定义为一个指向 string 类型对象的指针。

We say that this definition can be misleading because it encourages the belief that string* is the type and any variable defined in the same definition is a pointer to string. However,

这种指针声明风格容易引起这样的误解:把 string* 理解为一种数据类型,认为在同一声明语句中定义的其他变量也是指向 string 类型对象的指针。然而,语句

          string* ps1, ps2; // ps1 is a pointer to string,  ps2 is a string

defines ps1 as a pointer, but ps2 is a plain string. If we want to define two pointers in a single definition, we must repeat the * on each identifier:

实际上只把 ps1 定义为指针,而 ps2 并非指针,只是一个普通的 string 对象而已。如果需要在一个声明语句中定义两个指针,必须在每个变量标识符前再加符号 * 声明:

          string* ps1, *ps2; // both ps1 and ps2 are pointers to string
Multiple Pointer Declarations Can Be Confusing
连续声明多个指针易导致混淆

There are two common styles for declaring multiple pointers of the same type. One style requires that a declaration introduce only a single name. In this style, the * is placed with the type to emphasize that the declaration is declaring a pointer:

连续声明同一类型的多个指针有两种通用的声明风格。其中一种风格是一个声明语句只声明一个变量,此时,符号 * 紧挨着类型名放置,强调这个声明语句定义的是一个指针:

          string* ps1;
          string* ps2;

The other style permits multiple declarations in a single statement but places the * adjacent to the identifier. This style emphasizes that the object is a pointer:

另一种风格则允许在一条声明语句中声明多个指针,声明时把符号 * 靠近标识符放置。这种风格强调对象是一个指针:

          string *ps1, *ps2;

As with all questions of style, there is no single right way to declare pointers. The important thing is to choose a style and stick with it.

关于指针的声明,不能说哪种声明风格是唯一正确的方式,重要的是选择一种风格并持续使用。



In this book we use the second style and place the * with the pointer variable name.

在本书中,我们将采用第二种声明风格:将符号 * 紧贴着指针变量名放置。

Possible Pointer Values
指针可能的取值

A valid pointer has one of three states: It can hold the address of a specific object, it can point one past the end of an object, or it can be zero. A zero-valued pointer points to no object. An uninitialized pointer is invalid until it is assigned a value. The following definitions and assignments are all legal:

一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址;指向某个对象后面的另一对象;或者是0值。若指针保存0值,表明它不指向任何对象。未初始化的指针是无效的,直到给该指针赋值后,才可使用它。下列定义和赋值都是合法的:

          int ival = 1024;
          int *pi = 0;       // pi initialized to address no object
          int *pi2 = & ival; // pi2 initialized to address of ival
          int *pi3;          // ok, but dangerous, pi3 is uninitialized
          pi = pi2;          // pi and pi2 address the same object, e.g. ival
          pi2 = 0;           // pi2 now addresses no object
Avoid Uninitialized Pointers
避免使用未初始化的指针

Uninitialized pointers are a common source of run-time errors.

很多运行时错误都源于使用了未初始化的指针。

As with any other uninitialized variable, what happens when we use an uninitialized pointer is undefined. Using an uninitialized pointer almost always results in a run-time crash. However, the fact that the crash results from using an uninitialized pointer can be quite hard to track down.

就像使用其他没有初始化的变量一样,使用未初始化的指针时的行为C++标准中并没有定义使用未初始化的指针,它几乎总会导致运行时崩溃。然而,导致崩溃的这一原因很难发现。

Under most compilers, if we use an uninitialized pointer the effect will be to use whatever bits are in the memory in which the pointer resides as if it were an address. Using an uninitialized pointer uses this supposed address to manipulate the underlying data at that supposed location. Doing so usually leads to a crash as soon as we attempt to dereference the uninitialized pointer.

对大多数的编译器来说,如果使用未初始化的指针,会将指针中存放的不确定值视为地址,然后操纵该内存地址中存放的位内容。使用未初始化的指针相当于操纵这个不确定地址中存储的基础数据。因此,在对未初始化的指针进行解引用时,通常会导致程序崩溃。

It is not possible to detect whether a pointer is uninitialized. There is no way to distinguish a valid address from an address formed from the bits that are in the memory in which the pointer was allocated. Our recommendation to initialize all variables is particularly important for pointers.

C++ 语言无法检测指针是否未被初始化,也无法区分有效地址和由指针分配到的存储空间中存放的二进制位形成的地址。建议程序员在使用之前初始化所有的变量,尤其是指针。

If possible, do not define a pointer until the object to which it should point has been defined. That way, there is no need to define an uninitialized pointer.

如果可能的话,除非所指向的对象已经存在,否则不要先定义指针,这样可避免定义一个未初始化的指针。


If you must define a pointer separately from pointing it at an object, then initialize the pointer to zero. The reason is that a zero-valued pointer can be tested and the program can detect that the pointer does not point to an object.

如果必须分开定义指针和其所指向的对象,则将指针初始化为 0。因为编译器可检测出 0 值的指针,程序可判断该指针并未指向一个对象。


Constraints on Initialization of and Assignment to Pointers
指针初始化和赋值操作的约束

There are only four kinds of values that may be used to initialize or assign to a pointer:

对指针进行初始化或赋值只能使用以下四种类型的值:

  1. A constant expression (Section 2.7, p. 62) with value 0 (e.g., a const integral object whose value is zero at compile time or a literal constant 0)

    0 值常量表达式(第 2.7 节),例如,在编译时可获得 0 值的整型 const 对象或字面值常量 0。

  2. An address of an object of an appropriate type

    类型匹配的对象的地址。

  3. The address one past the end of another object

    另一对象末的下一地址。

  4. Another valid pointer of the same type

    同类型的另一个有效指针。

It is illegal to assign an int to a pointer, even if the value of the int happens to be 0. It is okay to assign the literal 0 or a const whose value is known to be 0 at compile time:

int 型变量赋给指针是非法的,尽管此 int 型变量的值可能为 0。但允许把数值 0 或在编译时可获得 0 值的 const 量赋给指针:

          int ival;
          int zero = 0;
          const int c_ival = 0;
          int *pi = ival; // error: pi initialized from int value of ival
          pi = zero;      // error: pi assigned int value of zero
          pi = c_ival;    // ok: c_ival is a const with compile-time value of 0
          pi = 0;         // ok: directly initialize to literal constant 0

In addition to using a literal 0 or a const with a compile-time value of 0, we can also use a facility that C++ inherits from C. The cstdlib header defines a preprocessor variable (Section 2.9.2, p. 69) named NULL, which is defined as 0. When we use a preprocessor variable in our code, it is automatically replaced by its value. Hence, initializing a pointer to NULL is equivalent to initializing it to 0:

除了使用数值0或在编译时值为 0 的 const 量外,还可以使用 C++ 语言从 C 语言中继承下来的预处理器变量 NULL第 2.9.2 节),该变量在 cstdlib 头文件中定义,其值为 0。如果在代码中使用了这个预处理器变量,则编译时会自动被数值 0 替换。因此,把指针初始化为 NULL 等效于初始化为 0 值:

          // cstdlib #defines NULL to 0
          int *pi = NULL; // ok: equivalent to int *pi = 0;

As with any preprocessor variable (Section 2.9.2, p. 71) we should not use the name NULL for our own variables.

正如其他的预处理器变量一样(第 2.9.2 节),不可以使用 NULL 这个标识符给自定义的变量命名。

Preprocessor variables are not defined in the std namespace and hence the name is NULL, not std::NULL.

预处理器变量不是在 std 命名空间中定义的,因此其名字应为 NULL,而非 std::NULL



With two exceptions, which we cover in Sections 4.2.5 and 15.3, we may only initialize or assign a pointer from an address or another pointer that has the same type as the target pointer:

除了将在第 4.2.5 节第 15.3 节介绍的两种例外情况之外,指针只能初始化或赋值为同类型的变量地址或另一指针:

          double dval;
          double *pd = &dval;   // ok: initializer is address of a double
          double *pd2 = pd;     // ok: initializer is a pointer to double

          int *pi = pd;   // error: types of pi and pd differ
          pi = &dval;     // error: attempt to assign address of a double to int *

The reason the types must match is that the type of the pointer is used to determine the type of the object that it addresses. Pointers are used to indirectly access an object. The operations that the pointer can perform are based on the type of the pointer: A pointer to int treats the underlying object as if it were an int. If that pointer actually addressed an object of some other type, such as double, then any operations performed by the pointer would be in error.

由于指针的类型用于确定指针所指对象的类型,因此初始化或赋值时必须保证类型匹配。指针用于间接访问对象,并基于指针的类型提供可执行的操作,例如,int 型指针只能把其指向的对象当作 int 型数据来处理,如果该指针确实指向了其他类型(如 double 类型)的对象,则在指针上执行的任何操作都有可能出错。

void* Pointers
void* 指针

The type void* is a special pointer type that can hold an address of any object:

C++ 提供了一种特殊的指针类型 void*,它可以保存任何类型对象的地址:

          double obj = 3.14;
          double *pd = &obj;
          // ok: void* can hold the address value of any data pointer type
          void *pv = &obj;       // obj can be an object of any type
          pv = pd;               // pd can be a pointer to any type

A void* indicates that the associated value is an address but that the type of the object at that address is unknown.

void* 表明该指针与一地址值相关,但不清楚存储在此地址上的对象的类型。

There are only a limited number of actions we can perform on a void* pointer: We can compare it to another pointer, we can pass or return it from a function, and we can assign it to another void* pointer. We cannot use the pointer to operate on the object it addresses. We'll see in Section 5.12.4 (p. 183) how we can retrieve the address stored in a void* pointer.

void* 指针只支持几种有限的操作:与另一个指针进行比较;向函数传递 void* 指针或从函数返回 void* 指针;给另一个 void* 指针赋值。不允许使用 void* 指针操纵它所指向的对象。我们将在第 5.12.4 节讨论如何重新获取存储在 void* 指针中的地址。

Exercises Section 4.2.2

Exercise 4.10:

Explain the rationale for preferring the first form of pointer declaration:

下面提供了两种指针声明的形式,解释宁愿使用第一种形式的原因:

          int *ip; // good practice
          int* ip; // legal but misleading
Exercise 4.11:

Explain each of the following definitions. Indicate whether any are illegal and if so why.

解释下列声明语句,并指出哪些是非法的,为什么?

          (a) int* ip;
          (b) string s, *sp = 0;
          (c) int i; double* dp = &i;
          (d) int* ip, ip2;
          (e) const int i = 0, *p = i;
          (f) string *p = NULL;
Exercise 4.12:

Given a pointer, p, can you determine whether p points to a valid object? If so, how? If not, why not?

已知一指针 p,你可以确定该指针是否指向一个有效的对象吗?如果可以,如何确定?如果不可以,请说明原因。

Exercise 4.13:

Why is the first pointer initialization legal and the second illegal?

下列代码中,为什么第一个指针的初始化是合法的,而第二个则不合法?

          int i = 42;
          void *p = &i;
          long *lp = &i;


4.2.3. Operations on Pointers

4.2.3. 指针操作

Pointers allow indirect manipulation of the object to which the pointer points. We can access the object by dereferencing the pointer. Dereferencing a pointer is similar to dereferencing an iterator (Section 3.4, p. 98). The * operator (the dereference operator) returns the object to which the pointer points:

指针提供间接操纵其所指对象的功能。与对迭代器进行解引用操作(第 3.4 节)一样,对指针进行解引用可访问它所指的对象,* 操作符(解引用操作符)将获取指针所指的对象:

          string s("hello world");
          string *sp = &s; // sp holds the address of s
          cout  <<*sp;     // prints hello world

When we dereference sp, we fetch the value of s. We hand that value to the output operator. The last statement, therefore, prints the contents of sthat is, hello world.

sp 进行解引用将获得 s 的值,然后用输出操作符输出该值,于是最后一条语句输出了 s 的内容 hello world

Dereference Yields an Lvalue
生成左值的解引用操作

The dereference operator returns the lvalue of the underlying object, so we can use it to change the value of the object to which the pointer points:

解引用操作符返回指定对象的左值,利用这个功能可修改指针所指对象的值:

          *sp = "goodbye"; // contents of s now changed

Because we assign to *sp, this statement leaves sp pointing to s and changes the value of s.

因为 sp 指向 s,所以给 *sp 赋值也就修改了 s 的值。

We can also assign a new value to sp itself. Assigning to sp causes sp to point to a different object:

也可以修改指针 sp 本身的值,使 sp 指向另外一个新对象:

          string s2 = "some value";
          sp = &s2;  // sp now points to s2

We change the value of a pointer by assigning to it directlywithout dereferencing the pointer.

给指针直接赋值即可修改指针的值——不需要对指针进行解引用。

Key Concept: Assigning TO or THROUGH a Pointer

关键概念:给指针赋值或通过指针进行赋值

When first using pointers, the difference in whether an assignment is to the pointer or through the pointer to the value pointed to can be confusing. The important thing to keep in mind is that if the left-hand operand is dereferenced, then the value pointed to is changed. If there is no dereference, then the pointer itself is being changed. A picture can sometimes help:

对于初学指针者,给指针赋值和通过指针进行赋值这两种操作的差别确实让人费解。谨记区分的重要方法是:如果对左操作数进行解引用,则修改的是指针所指对象的值;如果没有使用解引用操作,则修改的是指针本身的值。如图所示,帮助理解下列例子:


Comparing Pointers and References
指针和引用的比较

While both references and pointers are used to indirectly access another value, there are two important differences between references and pointers. The first is that a reference always refers to an object: It is an error to define a reference without initializing it. The behavior of assignment is the second important difference: Assigning to a reference changes the object to which the reference is bound; it does not rebind the reference to another object. Once initialized, a reference always refers to the same underlying object.

虽然使用引用(reference)和指针都可间接访问另一个值,但它们之间有两个重要区别。第一个区别在于引用总是指向某个对象:定义引用时没有初始化是错误的。第二个重要区别则是赋值行为的差异:给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。引用一经初始化,就始终指向同一个特定对象(这就是为什么引用必须在定义时初始化的原因)。

Consider these two program fragments. In the first, we assign one pointer to another:

考虑以下两个程序段。第一个程序段将一个指针赋给另一指针:

          int ival = 1024, ival2 = 2048;
          int *pi = &ival, *pi2 = &ival2;
          pi = pi2;    // pi now points to ival2

After the assignment, ival, the object addressed by pi remains unchanged. The assignment changes the value of pi, making it point to a different object. Now consider a similar program that assigns two references:

赋值结束后,pi 所指向的 ival 对象值保持不变,赋值操作修改了 pi 指针的值,使其指向另一个不同的对象。现在考虑另一段相似的程序,使用两个引用赋值:

          int &ri = ival, &ri2 = ival2;
          ri = ri2;    // assigns ival2 to ival

This assignment changes ival, the value referenced by ri, and not the reference itself. After the assignment, the two references still refer to their original objects, and the value of those objects is now the same as well.

这个赋值操作修改了 ri 引用的值 ival 对象,而并非引用本身。赋值后,这两个引用还是分别指向原来关联的对象,此时这两个对象的值相等。

Pointers to Pointers
指向指针的指针

Pointers are themselves objects in memory. They, therefore, have addresses that we can store in a pointer:

指针本身也是可用指针指向的内存对象。指针占用内存空间存放其值,因此指针的存储地址可存放在指针中。下面程序段:

          int ival = 1024;
          int *pi = &ival; // pi points to an int
          int **ppi = &pi; // ppi points to a pointer to int

which yields a pointer to a pointer. We designate a pointer to a pointer by using **. We might represent these objects as

定义了指向指针的指针。C++ 使用 ** 操作符指派一个指针指向另一指针。这些对象可表示为:

As usual, dereferencing ppi yields the object to which ppi points. In this case, that object is a pointer to an int:

ppi 进行解引用照常获得 ppi 所指的对象,在本例中,所获得的对象是指向 int 型变量的指针 pi

          int *pi2 = *ppi; // ppi points to a pointer

To actually access ival, we need to dereference ppi twice:

为了真正地访问到 ival 对象,必须对 ppi 进行两次解引用:

          cout << "The value of ival\n"
               << "direct value: " << ival << "\n"
               << "indirect value: " << *pi << "\n"
               << "doubly indirect value: " << **ppi
               << endl;

This program prints the value of ival three different ways. First, by direct reference to the variable. Then, through the pointer to int in pi, and finally, by dereferencing ppi twice to get to the underlying value in ival.

这段程序用三种不同的方式输出 ival 的值。首先,采用直接引用变量的方式输出;然后使用指向 int 型对象的指针 pi 输出;最后,通过对 ppi 进行两次解引用获得 ival 的特定值。

Exercises Section 4.2.3

Exercise 4.14:

Write code to change the value of a pointer. Write code to change the value to which the pointer points.

编写代码修改指针的值;然后再编写代码修改指针所指对象的值。

Exercise 4.15:

Explain the key differences between pointers and references.

解释指针和引用的主要区别。

Exercise 4.16:

What does the following program do?

下列程序段实现什么功能?

          int i = 42, j = 1024;
          int *p1 = &i, *p2 = &j;
          *p2 = *p1 * *p2;
          *p1 *= *p1;


4.2.4. Using Pointers to Access Array Elements

4.2.4. 使用指针访问数组元素

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]

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
Pointer Arithmetic
指针的算术操作

Rather than taking the address of the value returned by subscripting, we could use pointer arithmetic. Pointer arithmetic works the same way (and has the same constraints) as iterator arithmetic (Section 3.4.1, p. 100). Using pointer arithmetic, we can compute a pointer to an element by adding (or subtracting) an integral value to (or from) a pointer to another element in the array:

与其使用下标操作,倒不如通过指针的算术操作来获取指定内容的存储地址。指针的算术操作和迭代器的算术操作(第 3.4.1 节)以相同的方式实现(也具有相同的约束)。使用指针的算术操作在指向数组某个元素的指针上加上(或减去)一个整型数值,就可以计算出指向数组另一元素的指针值:

          ip = ia;            // ok: ip points to ia[0]
          int *ip2 = ip + 4;  // ok: ip2 points to ia[4], the last element in ia

When we add 4 to the pointer ip, we are computing a new pointer. That new pointer points to the element four elements further on in the array from the one to which ip currently points.

在指针 ip 上加 4 得到一个新的指针,指向数组中 ip 当前指向的元素后的第 4 个元素。

More generally, when we add (or subtract) an integral value to a pointer, the effect is to compute a new pointer. The new pointer points to the element as many elements as that integral value ahead of (or behind) the original pointer.

通常,在指针上加上(或减去)一个整型数值 n 等效于获得一个新指针,该新指针指向指针原来指向的元素之后(或之前)的第 n 个元素。

Pointer arithmetic is legal only if the original pointer and the newly calculated pointer address elements of the same array or an element one past the end of that array. If we have a pointer to an object, we can also compute a pointer that points just after that object by adding one to the pointer.

指针的算术操作只有在原指针和计算出来的新指针都指向同一个数组的元素,或指向该数组存储空间的下一单元时才是合法的。如果指针指向一对象,我们还可以在指针上加1从而获取指向相邻的下一个对象的指针。



Given that ia has 4 elements, adding 10 to ia would be an error:

假设数组 ia 只有 4 个元素,则在 ia 上加 10 是错误的:

          // error: ia has only 4 elements, ia + 10 is an invalid address
          int *ip3 = ia + 10;

We can also subtract two pointers as long as they point into the same array or to an element one past the end of the array:

只要两个指针指向同一数组或有一个指向该数组末端的下一单元,C++ 还支持对这两个指针做减法操作:

          ptrdiff_t n = ip2 - ip; // ok: distance between the pointers

The result is four, the distance between the two pointers, measured in objects. The result of subtracting two pointers is a library type named ptrdiff_t. Like size_t, the ptrdiff_t type is a machine-specific type and is defined in the cstddef header. The size_t type is an unsigned type, whereas ptrdiff_t is a signed integral type.

结果是 4,这两个指针所指向的元素间隔为 4 个对象。两个指针减法操作的结果是标准库类型(library type)ptrdiff_t 的数据。与 size_t 类型一样,ptrdiff_t 也是一种与机器相关的类型,在 cstddef 头文件中定义。size_tunsigned 类型,而 ptrdiff_t 则是 signed 整型。

The difference in type reflects how these two types are used: size_t is used to hold the size of an array, which must be a positive value. The ptrdiff_t type is guaranteed to be large enough to hold the difference between any two pointers into the same array, which might be a negative value. For example, had we subtracted ip2 from ip, the result would be -4.

这两种类型的差别体现了它们各自的用途:size_t 类型用于指明数组长度,它必须是一个正数;ptrdiff_t 类型则应保证足以存放同一数组中两个指针之间的差距,它有可能是负数。例如,ip 减去 ip2,结果为 -4

It is always possible to add or subtract zero to a pointer, which leaves the pointer unchanged. More interestingly, given a pointer that has a value of zero, it is also legal to add zero to that pointer. The result is another zero-valued pointer. We can also subtract two pointers that have a value of zero. The result of subtracting two zero-valued pointers is zero.

允许在指针上加减 0,使指针保持不变。更有趣的是,如果一指针具有 0 值(空指针),则在该指针上加 0 仍然是合法的,结果得到另一个值为 0 的指针。也可以对两个空指针做减法操作,得到的结果仍是 0。

Interaction between Dereference and Pointer Arithmetic
解引用和指针算术操作之间的相互作用

The result of adding an integral value to a pointer is itself a pointer. We can dereference the resulting pointer directly without first assigning it to another pointer:

在指针上加一个整型数值,其结果仍然是指针。允许在这个结果上直接进行解引用操作,而不必先把它赋给一个新指针:

          int last = *(ia + 4); // ok: initializes last to 8, the value of ia[4]

This expression calculates the address four elements past ia and dereferences that pointer. It is equivalent to writing ia[4].

这个表达式计算出 ia 所指向元素后面的第 4 个元素的地址,然后对该地址进行解引用操作,等价于 ia[4]

The parentheses around the addition are essential. Writing

加法操作两边用圆括号括起来是必要的。如果写为:


          last = *ia + 4;     // ok: last = 4, equivalent to ia[0]+4

means dereference ia and add four to the dereferenced value.

意味着对 ia 进行解引用,获得 ia 所指元素的值 ia[0],然后加 4。


The parentheses are required due to the precedence of the addition and dereference operators. We'll learn more about precedence in Section 5.10.1 (p. 168). Simply put, precedence stipulates how operands are grouped in expressions with multiple operators. The dereference operator has a higher precedence than the addition operator.

由于加法操作和解引用操作的优先级不同,上述表达式中的圆括号是必要的。我们将在第 5.10.1 节讨论操作符的优先级。简单地说,优先级决定了有多个操作符的表达式如何对操作数分组。解引用操作符的优先级比加法操作符高。

The operands to operators with higher precedence are grouped more tightly than those of lower precedence. Without the parentheses, the dereference operator would use ia as its operand. The expression would be evaluated by dereferencing ia and adding four to the value of the element at the beginning of ia.

与低优先级的操作符相比,优先级高的操作符的操作数先被组合起来操作。如果没有圆括号,解引用操作符的操作数是 ia,该表达式先对 ia 解引用,获得 ia 数组中的第一个元素,并将该值与 4 相加。

By parenthesizing the expression, we override the normal precedence rules and effectively treat (ia + 4) as a single operand. That operand is an address of an element four past the one to which ia points. That new address is dereferenced.

如果表达式加上圆括号,则不管一般的优先级规则,将 (ia + 4) 作为单个操作数,这是 ia 所指向的元素后面第4个元素的地址,然后对这个新地址进行解引用。

Subscripts and Pointers
下标和指针

We have already seen that when we use an array name in an expression, we are actually using a pointer to the first element in the array. This fact has a number of implications, which we shall point out as they arise.

我们已经看到,在表达式中使用数组名时,实际上使用的是指向数组第一个元素的指针。这种用法涉及很多方面,当它们出现时我们会逐一指出来。

One important implication is that 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

When we write ia[0], that is an expression that uses the name of an array. When we subscript an array, we are really subscripting a pointer to an element in that array. We can use the subscript operator on any pointer, as long as that pointer points to an element in an array:

ia[0] 是一个使用数组名的表达式。在使用下标访问数组时,实际上是对指向数组元素的指针做下标操作。只要指针指向数组元素,就可以对它进行下标操作:

          int *p = &ia[2];     // ok: p points to the element indexed by 2
          int j = p[1];        // ok: p[1] equivalent to *(p + 1),
                               //    p[1] is the same element as ia[3]
          int k = p[-2];       // ok: p[-2] is the same element as ia[0]
Computing an Off-the-End Pointer
计算数组的超出末端指针

When we use a vector, the end operation returns an iterator that refers just past the end of the vector. We often use this iterator as a sentinel to control loops that process the elements in the vector. Similarly, we can compute an off-the-end pointer value:

vector 类型提供的 end 操作将返回指向超出 vector 末端位置的一个迭代器。这个迭代器常用作哨兵,来控制处理 vector 中元素的循环。类似地,可以计算数组的超出末端指针的值:

          const size_t arr_size = 5;
          int arr[arr_size] = {1,2,3,4,5};
          int *p = arr;           // ok: p points to arr[0]
          int *p2 = p + arr_size; // ok: p2 points one past the end of arr
                                  //    use caution -- do not dereference!

In this case, we set p to point to the first element in arr. We then calculate a pointer one past the end of arr by adding the size of arr to the pointer value in p. When we add 5 to p, the effect is to calculate the address of that is five ints away from pin other words, p + 5 points just past the end of arr.

本例中,p 指向数组 arr 的第一个元素,在指针 p 上加数组长度即可计算出数组 arr 的超出末端指针。p 加 5 即得 p 所指向的元素后面的第五个 int 元素的地址——换句话说,p + 5指向数组的超出末端的位置。

It is legal to compute an address one past the end of an array or object. It is not legal to dereference a pointer that holds such an address. Nor is it legal to compute an address more than one past the end of an array or an address before the beginning of an array.

C++ 允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作。而计算数组超出末端位置之后或数组首地址之前的地址都是不合法的。



The address we calculated and stored in p2 acts much like the iterator returned from the end operation on vectors. The iterator we obtain from end denotes "one past the end" of the vector. We may not dereference that iterator, but we may compare it to another iterator value to see whether we have processed all the elements in the vector. Similarly, the value we calculated for p2 can be used only to compare to another pointer value or as an operand in a pointer arithmetic expression. If we attempt to dereference p2, the most likely result is that it would yield some garbage value. Most compilers, would treat the result of dereferencing p2 as an int, using whatever bits happened to be in memory at the location just after the last element in arr.

计算并存储在p2中的地址,与在 vector 上做 end 操作所返回的迭代器具有相同的功能。由 end 返回的迭代器标志了该 vector 对象的“超出末端位置”,不能进行解引用运算,但是可将它与别的迭代器比较,从而判断是否已经处理完 vector 中所有的元素。同理,p2 也只能用来与其他指针比较,或者用做指针算术操作表达式的操作数。对 p2 进行解引用将得到无效值。对大多数的编译器来说,会把对 p2 进行解引用的结果(恰好存储在 arr 数组的最后一个元素后面的内存中的二进制位)视为一个 int 型数据。

Printing the Elements of an Array
输出数组元素

Now we are ready to write a program that uses pointers:

用指针编写以下程序:

          const size_t arr_sz = 5;
          int int_arr[arr_sz] = { 0, 1, 2, 3, 4 };
          // pbegin points to first element, pend points just after the last
          for (int *pbegin = int_arr, *pend = int_arr + arr_sz;
                    pbegin != pend; ++pbegin)
              cout << *pbegin << ' '; // print the current element

This program uses a feature of the for loop that we have not yet used: We may define multiple variables inside the init-statement (Section 1.4.2, p. 14) of a for as long as the variables are defined using the same type. In this case, we're defining two int pointers named pbegin and pend.

这段程序使用了一个我们以前没有用过的 for 循环性质:只要定义的多个变量具有相同的类型,就可以在 for 循环的初始化语句第 1.4.2 节)中同时定义它们。本例在初始化语句中定义了两个 int 型指针 pbeginpend

We use these pointers to traverse the array. Like other built-in types, arrays have no member functions. Hence, there are no begin and end operations on arrays. Instead, we must position pointers to denote the first and one past the last elements ourselves. We do so in the initialization of our two pointers. We initialize pbegin to address the first element of int_arr and pend to one past the last element in the array:

C++ 允许使用指针遍历数组。和其他内置类型一样,数组也没有成员函数。因此,数组不提供 beginend 操作,程序员只能自己给指针定位,使之分别标志数组的起始位置和超出末端位置。可在初始化中实现这两个指针的定位:初始化指针 pbegin 指向 int_arr 数组的第一个元素,而指针 pend 则指向该数组的超出末端的位置:

The pointer pend serves as a sentinel, allowing the for loop to know when to stop. Each iteration of the for loop increments pbegin to address the next element. On the first trip through the loop, pbegin denotes the first element, on the second iteration, the second element, and so on. After processing the last element in the array, pbegin will be incremented once more and will then equal pend. At that point we know that we have iterated across the entire array.

指针 pend 是标志 for 循环结束的哨兵。for 循环的每次迭代都会使 pbegin 递增 1 以指向数组的下一个元素。第一次执行 for 循环时,pbegin 指向数组中的第一个元素;第二次循环,指向第二个元素;这样依次类推。当处理完数组的最后一个元素后,pbegin 再加 1 则与 pend 值相等,表示整个数组已遍历完毕。

Pointers Are Iterators for Arrays
指针是数组的迭代器

Astute readers will note that this program is remarkably similar to the program on page 99, which traversed and printed the contents of a vector of strings. The loop in that program

聪明的读者可能已经注意到这段程序与第 3.4 节的一段程序非常相像,该程序使用下面的循环遍历并输出一个 string 类型的 vector 的内容:

          // equivalent loop using iterators to reset all the elements in ivec to 0
          for (vector<int>::iterator iter = ivec.begin();
                                     iter != ivec.end(); ++iter)
              *iter = 0; // set element to which iter refers to 0

used iterators in much the same way that pointers are used in the program to print the contents of the array. This similarity is not a coincidence. In fact, the built-in array type has many of the properties of a library container, and pointers, when we use them in conjunction with arrays, are themselves iterators. We'll have much more to say about containers and iterators in Part II.

这段程序使用迭代器的方式就像上个程序使用指针实现输出数组内容一样。指针和迭代器的这个相似之处并不是巧合。实际上,内置数组类型具有标准库容器的许多性质,与数组联合使用的指针本身就是迭代器。在第二部分中,我们还会详细介绍容器和迭代器类型。

Exercises Section 4.2.4

Exercise 4.17:

Given that p1 and p2 point to elements in the same array, what does the following statement do?

已知 p1p2 指向同一个数组中的元素,下面语句实现什么功能?

          p1 += p2 - p1;

Are there any values of p1 or p2 that could make this code illegal?

p1p2 具有什么值时这个语句是非法的?

Exercise 4.18:

Write a program that uses pointers to set the elements in an array of ints to zero.

编写程序,使用指针把一个 int 型数组的所有元素设置为0。


4.2.5. Pointers and the const Qualifier

4.2.5. 指针和 const 限定符

There are two kinds of interactions between pointers and the const qualifier discussed in Section 2.4 (p. 56): We can have pointers to const objects and pointers that are themselves const. This section discusses both kinds of pointers.

第 2.4 节介绍了指针和 const 限定符之间的两种交互类型:指向 const 对象的指针和 const 指针。我们在本节中详细讨论这两类指针。

Pointers to const Objects
指向 const 对象的指针

The pointers we've seen so far can be used to change the value of the objects to which they point. But if we have a pointer to a const object, we do not want to allow that pointer to change the underlying, const value. The language enforces this property by requiring that pointers to const objects must take the constness of their target into account:

到目前为止,我们使用指针来修改其所指对象的值。但是如果指针指向 const 对象,则不允许用指针来改变其所指的 const 值。为了保证这个特性,C++ 语言强制要求指向 const 对象的指针也必须具有 const 特性:

          const double *cptr;  // cptr may point to a double that is const

Here cptr is a pointer to an object of type const double. The const qualifies the type of the object to which cptr points, not cptr itself. That is, cptr itself is not const. We need not initialize it and can assign a new value to it if we so desire. What we cannot do is use cptr to change the value to which it points:

这里的 cptr 是一个指向 double 类型 const 对象的指针,const 限定了 cptr 指针所指向的对象类型,而并非 cptr 本身。也就是说,cptr 本身并不是 const。在定义时不需要对它进行初始化,如果需要的话,允许给 cptr 重新赋值,使其指向另一个 const 对象。但不能通过 cptr 修改其所指对象的值:

          *cptr = 42;   // error: *cptr might be const

It is also a compile-time error to assign the address of a const object to a plain, nonconst pointer:

把一个 const 对象的地址赋给一个普通的、非 const 对象的指针也会导致编译时的错误:

          const double pi = 3.14;
          double *ptr = &pi;        // error: ptr is a plain pointer
          const double *cptr = &pi; // ok: cptr is a pointer to const

We cannot use a void* pointer (Section 4.2.2, p. 119) to hold the address of a const object. Instead, we must use the type const void* to hold the address of a const object:

不能使用 void* 指针(第 4.2.2 节)保存 const 对象的地址,而必须使用 const void* 类型的指针保存 const 对象的地址:

          const int universe = 42;
          const void *cpv = &universe; // ok: cpv is const
          void *pv = &universe;        // error: universe is const

A pointer to a const object can be assigned the address of a nonconst object, such as

允许把非 const 对象的地址赋给指向 const 对象的指针,例如:

          double dval = 3.14; // dval is a double; its value can be changed
          cptr = &dval;       // ok: but can't change dval through cptr

Although dval is not a const, any attempt to modify its value through cptr results in a compile-time error. When we declared cptr, we said that it would not change the value to which it points. The fact that it happens to point to a nonconst object is irrelevant.

尽管 dval 不是 const 对象,但任何企图通过指针 cptr 修改其值的行为都会导致编译时的错误。cptr 一经定义,就不允许修改其所指对象的值。如果该指针恰好指向非 const 对象时,同样必须遵循这个规则。

We cannot use a pointer to const to change the underlying object. However, if the pointer addresses a nonconst object, it is possible that some other action will change the object to which the pointer points.

不能使用指向 const 对象的指针修改基础对象,然而如果该指针指向的是一个非 const 对象,可用其他方法修改其所指的对象。


The fact that values to which a const pointer points can be changed is subtle and can be confusing. Consider:

事实是,可以修改 const 指针所指向的值,这一点常常容易引起误会。考虑:

          dval = 3.14159;       // dval is not const
          *cptr = 3.14159;      // error: cptr is a pointer to const
          double *ptr = &dval;  // ok: ptr points at non-const double
          *ptr = 2.72;          // ok: ptr is plain pointer
          cout << *cptr;        // ok: prints 2.72

In this case, cptr is defined as a pointer to const but it actually points at a nonconst object. Even though the object to which it points is nonconst, we cannot use cptr to change the object's value. Essentially, there is no way for cptr to know whether the object it points to is const, and so it treats all objects to which it might point as const.

在此例题中,指向 const 的指针 cptr 实际上指向了一个非 const 对象。尽管它所指的对象并非 const,但仍不能使用 cptr 修改该对象的值。本质上来说,由于没有方法分辩 cptr 所指的对象是否为 const,系统会把它所指的所有对象都视为 const

When a pointer to const does point to a nonconst, it is possible that the value of the object might change: After all, that value is not const. We could either assign to it directly or, as here, indirectly through another, plain nonconst pointer. It is important to remember that there is no guarantee that an object pointed to by a pointer to const won't change.

如果指向 const 的指针所指的对象并非 const,则可直接给该对象赋值或间接地利用普通的非 const 指针修改其值:毕竟这个值不是 const。重要的是要记住:不能保证指向 const 的指针所指对象的值一定不可修改。

It may be helpful to think of pointers to const as "pointers that think they point to const."

如果把指向 const 的指针理解为“自以为指向 const 的指针”,这可能会对理解有所帮助。


In real-world programs, pointers to const occur most often as formal parameters of functions. Defining a parameter as a pointer to const serves as a contract guaranteeing that the actual object being passed into the function will not be modified through that parameter.

在实际的程序中,指向 const 的指针常用作函数的形参。将形参定义为指向 const 的指针,以此确保传递给函数的实际对象在函数中不因为形参而被修改。

const Pointers
const 指针

In addition to pointers to const, we can also have const pointersthat is, pointers whose own value we may not change:

除指向 const 对象的指针外,C++ 语言还提供了 const 指针——本身的值不能修改:

          int errNumb = 0;
          int *const curErr = &errNumb; // curErr is a constant pointer

Reading this definition from right to left, we see that "curErr is a constant pointer to an object of type int." As with any const, we may not change the value of the pointerthat is, we may not make it point to any other object. Any attempt to assign to a constant pointereven assigning the same value back to curErris flagged as an error during compilation:

我们可以从右向左把上述定义语句读作“curErr 是指向 int 型对象的 const 指针”。与其他 const 量一样,const 指针的值不能修改,这就意味着不能使 curErr 指向其他对象。任何企图给 const 指针赋值的行为(即使给 curErr 赋回同样的值)都会导致编译时的错误:

          curErr = curErr; // error: curErr is const

As with any const, we must initialize a const pointer when we create it.

与任何 const 量一样,const 指针也必须在定义时初始化。

The fact that a pointer is itself const says nothing about whether we can use the pointer to change the value to which it points. Whether we can change the value pointed to depends entirely on the type to which the pointer points. For example, curErr addresses a plain, nonconst int. We can use curErr to change the value of errNumb:

指针本身是 const 的事实并没有说明是否能使用该指针修改它所指向对象的值。指针所指对象的值能否修改完全取决于该对象的类型。例如,curErr 指向一个普通的非常量 int 型对象 errNumb,则可使用 curErr 修改该对象的值:

          if (*curErr) {
              errorHandler();
              *curErr = 0; // ok: reset value of the object to which curErr is bound
          }
const Pointer to a const Object
指向 const 对象的 const 指针

We can also define a constant pointer to a constant object as follows:

还可以如下定义指向 const 对象的 const 指针:

          const double pi = 3.14159;
          // pi_ptr is const and points to a const object
          const double *const pi_ptr = &pi;

In this case, neither the value of the object addressed by pi_ptr nor the address itself can be changed. We can read its definition from right to left as "pi_ptr is a constant pointer to an object of type double defined as const."

本例中,既不能修改 pi_ptr 所指向对象的值,也不允许修改该指针的指向(即 pi_ptr 中存放的地址值)。可从右向左阅读上述声明语句:“pi_ptr 首先是一个 const 指针,指向 double 类型的 const 对象”。

Pointers and Typedefs
指针和 typedef

The use of pointers in typedefs (Section 2.6, p. 61) often leads to surprising results. Here is a question almost everyone answers incorrectly at least once. Given the following,

在 typedef(第 2.6 节)中使用指针往往会带来意外的结果。下面是一个几乎所有人刚开始时都会答错的问题。假设给出以下语句:

          typedef string *pstring;
          const pstring cstr;

what is the type of cstr? The simple answer is that it is a pointer to const pstring. The deeper question is: what underlying type does a pointer to const pstring represent? Many think that the actual type is

请问 cstr 变量是什么类型?简单的回答是 const pstring 类型的指针。进一步问:const pstring 指针所表示的真实类型是什么?很多人都认为真正的类型是:

          const string *cstr; // wrong interpretation of const pstring cstr

That is, that a const pstring would be a pointer to a constant string. But that is incorrect.

也就是说,const pstring 是一种指针,指向 string 类型的 const 对象,但这是错误的。

The mistake is in thinking of a typedef as a textual expansion. When we declare a const pstring, the const modifies the type of pstring, which is a pointer. Therefore, this definition declares cstr to be a const pointer to string. The definition is equivalent to

错误的原因在于将 typedef 当做文本扩展了。声明 const pstring 时,const 修饰的是 pstring 的类型,这是一个指针。因此,该声明语句应该是把 cstr 定义为指向 string 类型对象的 const 指针,这个定义等价于:

          // cstr is a const pointer to string
          string *const cstr; // equivalent to const pstring cstr

Advice: Understanding Complicated const Type Declarations

建议:理解复杂的 const 类型的声明

Part of the problem in reading const declarations arises because the const can go either before or after the type:

阅读 const 声明语句产生的部分问题,源于 const 限定符既可以放在类型前也可以放在类型后:

          string const s1;   // s1 and s2 have same type,
          const string s2;   // they're both strings that are const

When writing const definitions using typedefs, the fact that the const can precede the type can lead to confusion as to the actual type being defined:

typedefconst 类型定义时,const 限定符加在类型名前面容易引起对所定义的真正类型的误解:


          string s;
          typedef string *pstring;
          const pstring cstr1 = &s; // written this way the type
 is obscured
          pstring const cstr2 = &s; // all three decreations are
 the same type
          string *const cstr3 = &s; // they're all const pointers
 to string

Putting the const after pstring and reading the declaration from right to left makes it clearer that cstr2 is a const pstring, which in turn is a const pointer to string.

const 放在类型 pstring 之后,然后从右向左阅读该声明语句就会非常清楚地知道 cstr2const pstring 类型,即指向 string 对象的 const 指针。

Unfortunately, most readers of C++ programs expect to see the const before the type. As a result, it is probably a good idea to put the const first, respecting common practice. But it can be helpful in understanding declarations to rewrite them to put the const after the type.

不幸的是,大多数人在阅读 C++ 程序时都习惯看到 const 放在类型前面。于是为了遵照惯例,只好建议编程时把 const 放在类型前面。但是,把声明语句重写为置 const 于类型之后更便于理解。


Exercises Section 4.3

Exercise 4.19:

Explain the meaning of the following five definitions. Identify any illegal definitions.

解释下列 5 个定义的含义,指出其中哪些定义是非法的:

          (a) int i;
          (b) const int ic;
          (c) const int *pic;
          (d) int *const cpi;
          (e) const int *const cpic;

Exercise 4.20:

Which of the following initializations are legal? Explain why.

下列哪些初始化是合法的?为什么?

          (a) int i = -1;
          (b) const int ic = i;
          (c) const int *pic = &ic;
          (d) int *const cpi = &ic;
          (e) const int *const cpic = &ic;

Exercise 4.21:

Based on the definitions in the previous exercise, which of the following assignments are legal? Explain why.

根据上述定义,下列哪些赋值运算是合法的?为什么?

          (a) i = ic;
          (b) pic = &ic;
          (c) cpi = pic;
          (d) pic = cpic;
          (e) cpic = &ic;
          (f) ic = *cpic;


Team LiB
Previous Section Next Section