Team LiB
Previous Section Next Section

5.4. Assignment Operators

5.4. 赋值操作符

The left-hand operand of an assignment operator must be a nonconst lvalue. Each of these assignments is illegal:

赋值操作符的左操作数必须是非 const 的左值。下面的赋值语句是不合法的:

     int i, j, ival;
     const int ci = i;  // ok: initialization not assignment
     1024 = ival;       // error: literals are rvalues
     i + j = ival;      // error: arithmetic expressions are rvalues
     ci = ival;         // error: can't write to ci

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

The result of an assignment is the left-hand operand; the type of the result is the type of the left-hand operand.

赋值表达式的值是其左操作数的值,其结果的类型为左操作数的类型。

The value assigned to the left-hand operand ordinarily is the value that is in the right-hand operand. However, assignments where the types of the left and right operands differ may require conversions that might change the value being assigned. In such cases, the value stored in the left-hand operand might differ from the value of the right-hand operand:

通常,赋值操作将其右操作数的值赋给左操作数。然而,当左、右操作数的类型不同时,该操作实现的类型转换可能会修改被赋的值。此时,存放在左、右操作数里的值并不相同:

     ival = 0;        // result: type int value 0
     ival = 3.14159;  // result: type int value 3

Both these assignments yield values of type int. In the first case the value stored in ival is the same value as in its right-hand operand. In the second case the value stored in ival is different from the right-hand operand.

上述两个赋值语句都产生int类型的值,第一个语句中 ival 的值与右操作数的值相同;但是在第二个语句中,ival 的值则与右操作数的值不相同。

5.4.1. Assignment Is Right Associative

5.4.1. 赋值操作的右结合性

Like the subscript and dereference operators, assignment returns an lvalue. As such, we can perform multiple assignments in a single expression, provided that each of the operands being assigned is of the same general type:

与下标和解引用操作符一样,赋值操作也返回左值。同理,只要被赋值的每个操作数都具有相同的通用类型,C++语言允许将这多个赋值操作写在一个表达式中:

     int ival, jval;
     ival = jval = 0; // ok: each assigned 0

Unlike the other binary operators, the assignment operators are right associative. We group an expression with multiple assignment operators from right to left. In this expression, the result of the rightmost assignment (i.e., jval) is assigned to ival. The types of the objects in a multiple assignment either must be the same type or of types that can be converted (Section 5.12, p. 178) to one another:

与其他二元操作符不同,赋值操作具有右结合特性。当表达式含有多个赋值操作符时,从右向左结合。上述表达式,将右边赋值操作的结果(也就是 jval)赋给 ival。多个赋值操作中,各对象必须具有相同的数据类型,或者具有可转换(第 5.12 节)为同一类型的数据类型:

     int ival; int *pval;
     ival = pval = 0; // error: cannot assign the value of a pointer to an int
     string s1, s2;
     s1 = s2 = "OK";  // ok: "OK" converted to string

The first assignment is illegal because ival and pval are objects of different types. It is illegal even though zero happens to be a value that could be assigned to either object. The problem is that the result of the assignment to pval is a value of type int*, which cannot be assigned to an object of type int. On the other hand, the second assignment is fine. The string literal is converted to string, and that string is assigned to s2. The result of that assignment is s2, which is then assigned to s1.

第一个赋值语句是不合法的,因为 ivalpval 是不同类型的对象。虽然0值恰好都可以赋给这两个对象,但该语句仍然错误。因为问题在于给 pval 赋值的结果是一个 int* 类型的值,不能将此值赋给 int 类型的对象。另一方面,第二个赋值语句则是正确的。字符串字面值可以转换为 string 类型,string 类型的值可赋给 s2 变量。右边赋值操作的结果为 s2,再将此结果值赋给 s1

5.4.2. Assignment Has Low Precedence

5.4.2. 赋值操作具有低优先级

Inside a condition is another common place where assignment is used as a part of a larger expression. Writing an assignment in a condition can shorten programs and clarify the programmer's intent. For example, the following loop uses a function named get_value, which we assume returns int values. We can test those values until we obtain some desired valuesay, 42:

另一种通常的用法,是将赋值操作写在条件表达式中,把赋值操作用作长表达式的一部分。这种做法可缩短程序代码并阐明程序员的意图。例如,下面的循环调用函数 get_value,假设该函数返回 int 数值,通过循环检查这些返回值,直到获得需要的值为止——这里是 42:

     int i = get_value();  // get_value returns an int
     while (i != 42) {
         // do something ...
         i = get_value(); }

The program begins by getting the first value and storing it in i. Then it establishes the loop, which tests whether i is 42, and if not, does some processing. The last statement in the loop gets a value from get_value(), and the loop repeats. We can write this loop more succinctly as

首先,程序将所获得的第一个值存储在 i 中,然后建立循环检查i的值是否为 42,如果不是,则做某些处理。循环中的最后一条语句调用 get_value() 返回一个值,然后继续循环。该循环可更简洁地写为:

     int i;
     while ((i = get_value()) != 42) {
         // do something ...
     }

The condition now more clearly expresses our intent: We want to continue until get_value returns 42. The condition executes by assigning the result returned by get_value to i and then comparing the result of that assignment with 42.

现在,循环条件更清晰地表达了程序员的意图:持续循环直到 get_value 返回 42 为止。在循环条件中,将 get_value 返回的值赋给 i,然后判断赋值的结果是否为 42。

The additional parentheses around the assignment are necessary because assignment has lower precedence than inequality.

在赋值操作上加圆括号是必需的,因为赋值操作符的优先级低于不等操作符。



Without the parentheses, the operands to != would be the value returned from calling get_value and 42. The true or false result of that test would be assigned to iclearly not what we intended!

如果没有圆括号,操作符 != 的操作数则是调用 get_value 返回的值和 42,然后将该操作的结果 truefalse 赋给 i—— 显然这并不是我们想要的。

Beware of Confusing Equality and Assignment Operators
谨防混淆相等操作符和赋值操作符

The fact that we can use assignment in a condition can have surprising effects:

可在条件表达式中使用赋值操作,这个事实往往会带来意外的效果:

     if (i = 42)

This code is legal: What happens is that 42 is assigned to i and then the result of the assignment is tested. In this case, 42 is nonzero, which is interpreted as a true value. The author of this code almost surely intended to test whether i was 42:

此代码是合法的:将 42 赋给 i,然后检验赋值的结果。此时,42 为非零值,因此解释为 true。其实,程序员的目的显然是想判断i的值是否为 42:

     if (i == 42)

Bugs of this sort are notoriously difficult to find. Some, but not all, compilers are kind enough to warn about code such as this example.

这种类型的程序错误很难发现。有些(并非全部)编译器会为类似于上述例子的代码提出警告。

Exercises Section 5.4.2

Exercise 5.11:

What are the values of i and d after the each assignment:

请问每次赋值操作完成后,id 的值分别是多少?

     int i;   double d;
     d = i = 3.5;
     i = d = 3.5;
Exercise 5.12:

Explain what happens in each of the if tests:

解释每个 if 条件判断产生什么结果?

     if (42 = i)   // . . .
     if (i = 42)   // . . .

5.4.3. Compound Assignment Operators

5.4.3. 复合赋值操作符

We often apply an operator to an object and then reassign the result to that same object. As an example, consider the sum program from page 14:

我们常常在对某个对象做某种操作后,再将操作结果重新赋给该对象。例如,考虑第 1.4.2 节的求和程序:

     int sum = 0;
     // sum values from 1 up to 10 inclusive
     for (int val = 1; val <= 10; ++val)
         sum += val; // equivalent to sum = sum + val

This kind of operation is common not just for addition but for the other arithmetic operators and the bitwise operators. There are compound assignments for each of these operators. The general syntactic form of a compound assignment operator is

C++ 语言不仅对加法,而且还对其他算术操作符和位操作符提供了这种用法,称为复合赋值操作。复合赋值操作符的一般语法格式为:

     a op= b;

where op= may be one of the following ten operators:

其中,op= 可以是下列十个操作符之一:

     +=   -=   *=   /=   %=   // arithmetic operators
     <<= >>=   &=   ^=   |=   // bitwise operators

Each compound operator is essentially equivalent to

这两种语法形式存在一个显著的差别:使用复合赋值操作时,左操作数只计算了一次;而使用相似的长表达式时,该操作数则计算了两次,第一次作为右操作数,而第二次则用做左操作数。除非考虑可能的性能价值,在很多(可能是大部分的)上下文环境里这个差别不是本质性的。

     a = a op b;

There is one important difference: When we use the compound assignment, the left-hand operand is evaluated only once. If we write the similar longer version, that operand is evaluated twice: once as the right-hand operand and again as the left. In many, perhaps most, contexts this difference is immaterial aside from possible performance consequences.

这两种语法形式存在一个显著的差别:使用复合赋值操作时,左操作数只计算了一次;而使用相似的长表达式时,该操作数则计算了两次,第一次作为右操作数,而第二次则用做左操作数。除非考虑可能的性能价值,在很多(可能是大部分的)上下文环境里这个差别不是本质性的。

Exercises Section 5.4.3

Exercise 5.13:

The following assignment is illegal. Why? How would you correct it?

下列赋值操作是不合法的,为什么?怎样改正?

     double dval; int ival; int *pi;
     dval = ival = pi = 0;
Exercise 5.14:

Although the following are legal, they probably do not behave as the programmer expects. Why? Rewrite the expressions as you think they should be.

虽然下列表达式都是合法的,但并不是程序员期望的操作,为什么?怎样修改这些表达式以使其能反映程序员的意图?

     (a) if (ptr = retrieve_pointer() != 0)
     (b) if (ival = 1024)
     (c) ival += ival + 1;

Team LiB
Previous Section Next Section