表达式简介

表达式的基本概念

考虑以下一系列语句,每个语句都定义了一个变量并对其进行了初始化:

// five() 是一个返回值 5 的函数
int five()
{
    return 5;
}

int main()
{
    int a{ 2 };             // 使用字面量值 2 初始化变量 a
    int b{ 2 + 3 };         // 使用计算值 5 初始化变量 b
    int c{ (2 * 3) + 4 };   // 使用计算值 10 初始化变量 c
    int d{ b };             // 使用变量值 5 初始化变量 d
    int e{ five() };        // 使用函数返回值 5 初始化变量 e

    return 0;
}

注意,上述初始化器使用了各种不同的实体:字面量、变量、操作符和函数调用。C++ 以某种方式将所有这些不同的东西转换成一个单一的值,然后可以用作变量的初始值。 所有这些初始化器有什么共同点?它们都使用了表达式。 在一般的编程中,表达式是由字面量、变量、操作符和函数调用组成的非空序列,用于计算一个值。执行表达式的过程称为求值,产生的结果值称为表达式的结果(有时也称为返回值)。

高级表达式概念

对于高级读者 在 C++ 中,表达式的结果可能是以下之一:

一个值(最常见的)

一个对象或一个函数。我们在第 12.2 课——值类别(左值和右值)中讨论返回对象的表达式。

无。这些是非返回值函数调用(在第 2.3 课——空函数(非返回值函数)中介绍)的结果,它们仅被调用以产生副作用。

目前,为了保持简单,我们假设表达式被求值以产生值。

表达式求值过程

当一个表达式被求值时,表达式中的每个项都被求值,直到剩下一个单一的值。以下是一些不同类型的表达式示例,注释表明它们如何求值:

2               // 2 是一个字面量,求值为值 2
"Hello world!"  // "Hello world!" 是一个字面量,求值为文本 "Hello world!"
x               // x 是一个变量,求值为变量 x 持有的值
2 + 3           // 操作符+ 使用操作数 2 和 3 求值为值 5
five()          // 求值为函数 five() 的返回值

如你所见,字面量求值为它们自己的值。变量求值为变量的值。操作符(如操作符+)使用它们的操作数求值为其他值。我们还没有介绍函数调用,但在表达式的上下文中,函数调用求值为函数返回的任何值。

带有副作用的表达式

对于高级读者 涉及具有副作用的操作符的表达式稍微复杂一些:

x = 5           // x = 5 具有将 5 赋值给 x 的副作用,求值为 x
x = 2 + 3       // 具有将 5 赋值给 x 的副作用,求值为 x
std::cout << x  // 具有将 x 的值打印到控制台的副作用,求值为 std::cout

表达式的使用场景

关键见解 在 C++ 中,任何预期单个值的地方,你都可以使用一个产生值的表达式来替代,表达式将被求值以产生一个单一的值。

表达式不以分号结束,也不能单独编译。例如,如果你尝试编译表达式 x = 5,你的编译器会抱怨(可能是关于缺少分号)。相反,表达式总是作为语句的一部分被求值。 例如,考虑这个语句:

int x{ 2 + 3 }; // 2 + 3 是一个没有分号的表达式——分号在包含表达式的语句的末尾

如果你将这个语句分解为其语法,它看起来像这样:

type identifier { expression };

type 可以是任何有效类型(我们选择了 int)。identifier 可以是任何有效名称(我们选择了 x)。而 expression 可以是任何有效表达式(我们选择了 2 + 3,它使用了两个字面量和一个操作符)。

表达式语句

某些表达式(如 x = 5)主要用于它们的副作用(在这种情况下,是将值 5 赋给变量 x)而不是它们产生的值。

相关内容 我们在第 1.9 课——字面量和操作符简介中介绍了副作用。

然而,我们上面提到表达式不能单独执行——它们必须作为语句的一部分存在。幸运的是,将任何表达式转换为等效的语句是微不足道的。表达式语句是一个由表达式后跟分号组成的语句。当执行表达式语句时,表达式将被求值。 因此,我们可以将任何表达式(如 x = 5)转换为一个将被编译的表达式语句(x = 5;)。 当表达式在表达式语句中使用时,表达式产生的任何结果都被丢弃(因为它没有被使用)。例如,当表达式 x = 5 求值时,操作符=的返回值被丢弃。这没关系,因为我们只是想将 5 赋给 x。

无用的表达式语句

我们也可以制作编译但没有效果的表达式语句。例如,表达式语句(2 * 3;)是一个表达式语句,其表达式求值为结果值 6,然后被丢弃。虽然在语法上有效,但这样的表达式语句是无用的。一些编译器(如 gcc 和 Clang)如果能够检测到表达式语句是无用的,会产生警告。

子表达式、完整表达式和复合表达式

我们偶尔需要讨论特定类型的表达式。为此,我们将定义一些相关术语。 考虑以下表达式:

2               // 2 是一个字面量,求值为值 2
2 + 3           // 2 + 3 使用操作符+求值为值 5
x = 4 + 5       // 4 + 5 求值为值 9,然后赋给变量 x

简化一点,子表达式是用作操作数的表达式。例如,x = 4 + 5 的子表达式是 x 和 4 + 5。4 + 5 的子表达式是 4 和 5。 一个完整表达式是一个不是子表达式的表达式。上述所有三个表达式(2、2 + 3 和 x = 4 + 5)都是完整表达式。

在非正式语言中,复合表达式是一个包含两个或更多操作符使用的表达式。x = 4 + 5 是一个复合表达式,因为它包含两个操作符的使用(操作符=和操作符+)。2 和 2 + 3 不是复合表达式。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"