1.9 — 字面量和运算符简介

字面量

考虑以下两个语句

std::cout << "Hello world!";
int x { 5 };

“Hello world!” 和 ‘5’ 是什么?它们是字面量。**字面量**(也称为**字面常量**)是直接插入到源代码中的固定值。

字面量和变量都具有值(和类型)。与变量(其值可以通过初始化和赋值分别设置和更改)不同,字面量的值是固定的,不能更改。字面量 `5` 始终具有值 `5`。这就是字面量被称为常量的原因。

为了进一步强调字面量和变量之间的区别,让我们看看这个小程序

#include <iostream>

int main()
{
    std::cout << 5 << '\n'; // print the value of a literal

    int x { 5 };
    std::cout << x << '\n'; // print the value of a variable
    return 0;
}

在第5行,我们将值 `5` 打印到控制台。当编译器编译此代码时,它将生成导致 `std::cout` 打印值 `5` 的代码。此值 `5` 被编译到可执行文件中,可以直接使用。

在第7行,我们创建了一个名为 `x` 的变量,并将其初始化为值 `5`。编译器将生成将字面值 `5` 复制到分配给 `x` 的任何内存位置的代码。在第8行,当我们打印 `x` 时,编译器将生成导致 `std::cout` 打印 `x` 内存位置处的值(其值为 `5`)的代码。

因此,两个输出语句都做相同的事情(打印值 5)。但在字面量的情况下,值 `5` 可以直接打印。在变量的情况下,值 `5` 必须从变量所代表的内存中获取。

这也解释了为什么字面量是常量而变量可以更改。字面量的值直接放置在可执行文件中,并且可执行文件本身在创建后不能更改。变量的值放置在内存中,并且内存的值可以在可执行文件运行时更改。

关键见解

字面量是直接插入到源代码中的值。这些值通常直接出现在可执行代码中(除非它们被优化掉)。

对象和变量表示保存值的内存位置。这些值可以按需获取。

相关内容

我们将在课程 5.2 -- 字面量 中详细讨论字面量。

运算符

在数学中,**操作**是涉及零个或多个输入值(称为**操作数**)并产生新值(称为*输出值*)的过程。要执行的特定操作由称为**运算符**的符号表示。

例如,作为孩子,我们都学习过 *2 + 3* 等于 *5*。在这种情况下,字面量 *2* 和 *3* 是操作数,符号 *+* 是运算符,它告诉我们对操作数应用数学加法以产生新值 *5*。

在 C++ 中,操作按您期望的方式工作。例如

#include <iostream>

int main()
{
    std::cout << 1 + 2 << '\n';

    return 0;
}

在此程序中,字面量 `1` 和 `2` 是加号 (`+`) 运算符的操作数,它产生输出值 `3`。此输出值然后打印到控制台。在 C++ 中,操作的输出值通常称为**返回值**。

您可能已经非常熟悉数学中常用标准算术运算符,包括加法 (`+`)、减法 (`-`)、乘法 (`*`) 和除法 (`/`)。在 C++ 中,赋值 (`=`) 也是一个运算符,插入 (`<<`)、提取 (`>>`) 和相等 (`==`) 也是如此。虽然大多数运算符都有符号作为名称(例如 `+` 或 `==`),但也有许多运算符是关键字(例如 `new`、`delete` 和 `throw`)。

作者注

出于当我们更详细地讨论运算符时将变得清楚的原因,对于符号运算符,通常将运算符的符号附加到单词 *operator* 之后。

例如,加法运算符将写为 `operator+`,提取运算符将写为 `operator>>`。

运算符作为输入的操作数数量称为运算符的**元数**。很少有人知道这个词是什么意思,所以不要在谈话中说出来,期望任何人都能理解你在说什么。C++ 中的运算符有四种不同的元数

**一元**运算符作用于一个操作数。一元运算符的示例是 `-` 运算符。例如,给定 `-5`,`operator-` 接受字面量操作数 `5` 并翻转其符号以产生新输出值 `-5`。

**二元**运算符作用于两个操作数(通常称为 *左* 和 *右*,因为左操作数出现在运算符的左侧,右操作数出现在运算符的右侧)。二元运算符的示例是 `+` 运算符。例如,给定 `3 + 4`,`operator+` 接受左操作数 `3` 和右操作数 `4` 并应用数学加法以产生新输出值 `7`。插入 (`<<`) 和提取 (`>>`) 运算符是二元运算符,左侧是 `std::cout` 或 `std::cin`,右侧是要输出的值或要输入的变量。

**三元**运算符作用于三个操作数。C++ 中只有一种(条件运算符),我们将在稍后介绍。

**零元**运算符作用于零个操作数。C++ 中也只有一种(抛出运算符),我们也将稍后介绍。

请注意,某些运算符根据其使用方式具有多个含义。例如,`operator-` 有两个上下文。它可以以一元形式用于反转数字的符号(例如,将 `5` 转换为 `-5`,反之亦然),也可以以二元形式用于执行减法(例如 `4 - 3`)。

链式运算符

运算符可以链接在一起,以便一个运算符的输出可以用作另一个运算符的输入。例如,给定以下表达式:`2 * 3 + 4`,乘法运算符首先执行,将左操作数 `2` 和右操作数 `3` 转换为返回值 `6`(它成为加法运算符的左操作数)。接下来,加法运算符执行,并将左操作数 `6` 和右操作数 `4` 转换为新值 `10`。

当我们深入探讨运算符主题时,我们将更多地讨论运算符的执行顺序。目前,了解算术运算符的执行顺序与标准数学中的执行顺序相同就足够了:先括号,然后指数,然后乘法和除法,然后加法和减法。这种排序有时缩写为 *PEMDAS*,或扩展为助记符“请原谅我亲爱的萨利阿姨”(Please Excuse My Dear Aunt Sally)。

作者注

在某些国家,PEMDAS 被教作 PEDMAS、BEDMAS、BODMAS 或 BIDMAS。

返回值和副作用

C++ 中的大多数运算符只使用其操作数来计算返回值。例如,`-5` 产生返回值 `-5`,`2 + 3` 产生返回值 `5`。有一些运算符不产生返回值(例如 `delete` 和 `throw`)。我们将在稍后介绍它们的作用。

某些运算符具有额外的行为。一个运算符(或函数)如果除了产生返回值之外还有一些可观察到的效果,则称其具有**副作用**。例如,`x = 5` 具有将值 `5` 赋值给变量 `x` 的副作用。即使运算符执行完毕后,`x` 的更改值也是可观察的(例如,通过打印 `x` 的值)。`std::cout << 5` 具有将 `5` 打印到控制台的副作用。即使 `std::cout << 5` 执行完毕后,我们也可以观察到 `5` 已打印到控制台的事实。

命名法

在日常语言中,“副作用”一词通常用于表示某些其他事情发生(例如服用药物)的次要(通常是负面或意外的)结果。例如,服用口服抗生素的常见副作用是腹泻。因此,我们通常认为副作用是我们要避免的事情,或者是主要目标之外的附带事情。

在 C++ 中,“副作用”一词具有不同的含义:它是运算符或函数除了产生返回值之外的可观察效果。

由于赋值具有更改对象值的可观察效果,这被认为是副作用。我们主要使用某些运算符(例如赋值运算符)是为了它们的副作用(而不是这些运算符产生的返回值)。在这种情况下,副作用既有益又可预测(并且返回值通常是附带的)。

致进阶读者

对于我们主要为了它们的返回值而调用的运算符(例如 `operator+` 或 `operator*`),它们的返回值通常是显而易见的(例如,操作数的和或乘积)。

对于我们主要为了它们的副作用而调用的运算符(例如 `operator=` 或 `operator<<`),它们产生的返回值(如果有的话)并不总是显而易见的。例如,您期望 `x = 5` 的返回值是什么?

`operator=` 和 `operator<<`(当用于将值输出到控制台时)都返回它们的左操作数。因此,`x = 5` 返回 `x`,`std::cout << 5` 返回 `std::cout`。这样做是为了这些运算符可以链式使用。

例如,`x = y = 5` 计算为 `x = (y = 5)`。首先 `y = 5` 将 `5` 赋值给 `y`。此操作然后返回 `y`,然后可以将其赋值给 `x`。

`std::cout << "Hello " << "world!"` 计算为 `(std::cout << "Hello ") << "world!"`。这首先将 `"Hello "` 打印到控制台。此操作返回 `std::cout`,然后可以使用它将 `"world!"` 也打印到控制台。

我们将在课程 6.1 -- 运算符优先级和结合性 中详细讨论运算符的求值顺序。

小测验时间

问题 #1


对于以下各项,指示它们产生什么输出

a)

std::cout << 3 + 4 << '\n';

显示答案

b)

std::cout << 3 + 4 - 5 << '\n';

显示答案

c)

std::cout << 2 + 3 * 4 << '\n';

显示答案

d) 额外加分

int x { 2 };
std::cout << (x = 5) << '\n';

显示答案

guest
您的电子邮箱地址将不会被显示
发现错误?请在上方留言!
与勘误相关的评论在处理后将被删除,以帮助减少混乱。感谢您帮助使网站对每个人都更好!
来自 https://gravatar.com/ 的头像与您提供的电子邮箱地址相关联。
有回复时通知我:  
306 条评论
最新
最早 最多投票
内联反馈
查看所有评论