1-10 表达式介绍
表达式
考虑以下一系列语句,每条语句都定义了一个变量并对其进行初始化:
// five() is a function that returns the value 5
int five()
{
return 5;
}
int main()
{
int a{ 2 }; // initialize variable a with literal value 2
int b{ 2 + 3 }; // initialize variable b with computed value 5
int c{ (2 * 3) + 4 }; // initialize variable c with computed value 10
int d{ b }; // initialize variable d with variable value 5
int e{ five() }; // initialize variable e with function return value 5
return 0;
}
请注意,上述初始化器使用了多种不同的元素:字面量、变量、运算符和函数调用。C++ 通过某种机制将这些不同元素转换为单一值,从而可作为变量的初始值。
这些初始化器有何共同点?它们都使用了表达式。
在编程中,表达式expression是指由字面量、变量、运算符和函数调用组成的非空序列,用于计算某个值。执行表达式的过程称为求值evaluation,最终生成的值称为表达式结果result (有时也称返回值return value)。
对于高级读者
在C++中,表达式的结果属于以下类型之一:
- 值(最常见)
- 对象或函数。第12.2节——值类别(左值与右值)将讨论返回对象的表达式。
- 无。此类表达式为不返回值的函数调用(详见第2.3课——空函数(不返回值的函数)),仅为实现副作用而调用
为简化说明,当前阶段我们假设所有表达式评估后均产生值。
当表达式被求值时,表达式内部的每个项都会被求值,直到只剩下单一值。以下是不同类型表达式的示例,注释说明了它们的求值过程:
2 // 2 is a literal that evaluates to value 2
"Hello world!" // "Hello world!" is a literal that evaluates to text "Hello world!"
x // x is a variable that evaluates to the value held by variable x
2 + 3 // operator+ uses operands 2 and 3 to evaluate to value 5
five() // evaluates to the return value of function five()
如你所见,字面量会求值为其自身的值。变量会求值为该变量的值。运算符(如加法运算符)则利用其操作数来求值为其他值。虽然我们尚未涉及函数调用,但在表达式上下文中,函数调用会求值为该函数返回的任意值。
对于高级读者
涉及具有副作用的运算符的表达式会稍显棘手x = 5 // x = 5 has side effect of assigning 5 to x, evaluates to x x = 2 + 3 // has side effect of assigning 5 to x, evaluates to x std::cout << x // has side effect of printing value of x to console, evaluates to std::cout
关键要点
在C++中,凡是预期返回单一值的位置,均可使用生成值的表达式替代,该表达式将被求值以生成单一值。
表达式不以分号结尾,且无法单独编译。例如,若尝试编译表达式 x = 5,编译器会报错(通常是缺少分号)。实际上,表达式始终作为语句的一部分进行求值。
例如,考虑以下语句:
int x{ 2 + 3 }; // 2 + 3 is an expression that has no semicolon -- the semicolon is at the end of the statement containing the expression
若将此语句分解为语法结构,其形式如下:
类型 标识符 { 表达式 }; type identifier { expression };
其中类型可为任意有效类型(此处选取 int),标识符可为任意有效名称(此处选取 x),表达式可为任意有效表达式(此处选取 2 + 3,该表达式包含两个字面量和一个运算符)。
表达式语句
某些表达式(例如 x = 5)主要用于实现其副作用(在此例中,是将值 5 赋给变量 x),而非其产生的值。
相关内容:
我们在第1.9课——字面量与运算符介绍中介绍了副作用。
然而,我们之前提到表达式不能单独执行——它们必须作为语句的一部分存在。幸运的是,将任何表达式转换为等效语句非常简单。表达式语句是由表达式后跟分号组成的语句。当表达式语句被执行时,表达式将被求值。
因此,我们可以将任意表达式(如 x = 5)转换为可编译的表达式语句(x = 5;)。
当表达式用于表达式语句时,其计算结果会被丢弃(因未被使用)。例如表达式 x = 5 计算时,赋值运算符的返回值会被忽略。这完全合理,因为我们本意就是将 5 赋值给 x。
无用表达式语句
我们还可以编写能够编译但毫无作用的表达式语句。例如表达式语句 (2 * 3;) 就是其中一例:其表达式计算结果为 6,但该结果值随即被丢弃。尽管语法上有效,此类表达式语句毫无意义。某些编译器(如 gcc 和 Clang)若能检测到表达式语句无用,会发出警告。
子表达式、完整表达式和复合表达式
我们有时需要讨论特定类型的表达式。为此,我们将定义一些相关术语。
考虑以下表达式:
2 // 2 is a literal that evaluates to value 2
2 + 3 // 2 + 3 uses operator+ to evaluate to value 5
x = 4 + 5 // 4 + 5 evaluates to value 9, which is then assigned to variable x
简而言之,子表达式subexpression是指作为操作数使用的表达式。例如,x = 4 + 5 的子表达式是 x 和 4 + 5。4 + 5 的子表达式则是 4 和 5。
完整表达式full expression是指不属于子表达式的表达式。上述三个表达式(2、2 + 3 和 x = 4 + 5)均为完整表达式。
在日常语言中,复合表达式compound expression是指包含两个或更多运算符使用的表达式。x = 4 + 5 属于复合表达式,因为它包含两个运算符的使用(赋值运算符 = 和加法运算符 +)。而 2 和 2 + 3 则不属于复合表达式。
测验时间
问题 #1
语句和表达式有什么区别?
显示解答
当我们希望程序执行某个操作时,使用语句;当我们希望程序计算某个值时,使用表达式。
问题 #2
判断下列每行语句属于不含表达式的语句、含表达式的语句还是表达式语句。
a)
int x;
显示答案
该语句不包含表达式。这是一个变量定义语句。在此上下文中,x 并非表达式,因为它正在被定义而非被求值。
b)
int x = 5;
显示答案
语句包含一个表达式。int x 是变量定义。等号是复制初始化语法的一部分。等号右侧的初始化表达式是一个表达式。
c)
x = 5;
显示答案
表达式语句。x = 5 是对赋值运算符的调用,包含两个操作数:x 和 5。分号使其成为表达式语句。
d) 附加题:
foo(); // foo is a function
显示答案
函数调用是表达式的一部分,因此这是一个表达式语句。
e) 附加题:
std::cout << x; // Hint: operator<< is a binary operator.
显示答案
运算符<<是二元运算符,因此std::cout必须作为左操作数,x必须作为右操作数。由于这是整个语句,因此它必须是一个表达式语句。
问题 #3
请确定以下程序的输出结果。无需编译此程序,只需在脑海中逐行推导即可。
#include <iostream>
int main()
{
std::cout << 2 + 3 << '\n';
int x{ 6 };
int y{ x - 2 };
std::cout << y << '\n';
int z{};
z = x;
std::cout << z * x << '\n';
return 0;
}
显示答案
5
4
36

浙公网安备 33010602011771号