1-9 字面量与运算符介绍
字面量
考虑以下两个语句:
std::cout << "Hello world!";
int x { 5 };
什么是“Hello world!”和“5”?它们是字面量literal。字面量(也称为字面常量literal constant)是直接嵌入源代码中的固定值。
字面量和变量都具有值(以及类型)。不同于变量(其值可通过初始化和赋值分别设置和更改),字面量的值是固定的且不可更改。字面量 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节——字面量中将更深入地探讨字面量。
运算符
在数学中,运算是一种涉及零个或多个输入值(称为操作数operands)的过程,该过程会产生一个新值(称为输出值)。要执行的具体运算由一个称为运算符operator的符号表示。
例如,孩童时期我们都学过2+3=5。在此例中,数字2和3是操作数,符号+则是运算符——它指示我们对操作数执行数学加法运算,从而得出新值5。
在C++中,运算符的行为符合预期。例如:
#include <iostream>
int main()
{
std::cout << 1 + 2 << '\n';
return 0;
}
在此程序中,常量 1 和 2 是加号运算符 (+) 的操作数,该运算符产生输出值 3。该输出值随后被打印到控制台。在 C++ 中,运算的输出值通常称为返回值 return value。
您可能已经非常熟悉数学中常用的标准算术运算符,包括加法addition(+)、减法subtraction(-)、乘法multiplication(*)和除法division(/)。在C++中,赋值assignment(=)同样属于运算符,此外还包括插入运算符insertion(<<)、提取运算符extraction(>>)和相等运算符extraction()。虽然大多数运算符都有专属符号(如+或),但也有若干运算符属于关键字(如new、delete和throw)。
作者注
出于将在详细讨论运算符时阐明的缘由,对于符号类型的运算符,通常会在单词“operator”后附加运算符符号。例如,加法运算符将写为operator+,提取运算符则写为operator>>。
运算符作为输入所取的操作数数量称为该运算符的元数。很少有人知道这个词的含义,所以别在对话中突然抛出这个词,指望别人明白你在说什么。C++中的运算符有四种不同的元数:
一元Unary运算符作用于单个操作数。例如减号运算符(-)。以-5为例,运算符-将字面操作数5的符号翻转,生成新输出值-5。
二元Binary运算符作用于两个操作数(常称左操作数和右操作数,因左操作数位于运算符左侧,右操作数位于运算符右侧)。二元运算符的典型示例是加号运算符(+)。例如,在3 + 4的运算中,加号运算符将左操作数3与右操作数4进行数学加法运算,生成新输出值7。插入运算符(<<)和提取运算符(>>)属于二元运算符,其左侧接收std::cout或std::cin,右侧接收待输出的值或待输入的变量。
三元Ternary运算符作用于三个操作数。C++中仅有一种此类运算符(条件运算符),我们将在后续章节中介绍。
零元Nullary运算符作用于零个操作数。C++中同样仅有一种此类运算符(抛出运算符),我们也将稍后介绍。
需注意某些运算符根据使用方式具有多重含义。例如减号运算符-具有两种语境:可作为一元运算符取反数值符号(如将5转为-5或反之),也可作为二元运算符执行减法运算(如4 - 3)。
运算符链式调用
运算符可进行链式调用,即一个运算符的输出可作为另一个运算符的输入。例如,在表达式 2 * 3 + 4 中,乘法运算符优先执行,将左操作数 2 与右操作数 3 转换为返回值 6(该值成为加法运算符的左操作数)。随后加法运算符执行,将左操作数6与右操作数4转换为新值10。
关于运算符的执行顺序,我们将在深入探讨运算符主题时详述。目前只需理解:算术运算符遵循标准数学运算顺序——括号Parenthesis优先,其次是指数运算Exponents,再是乘Multiplication除Division运算,最后是加Addition减Subtraction运算。该顺序常简写为PEMDAS,或展开为助记词“请原谅我亲爱的莎莉阿姨Please Excuse My Dear Aunt Sally”。
作者注:
在某些国家,PEMDAS的教学形式可能被替换为PEDMAS、BEDMAS、BODMAS或BIDMAS。
返回值与副作用
C++ 中大多数运算符仅利用其操作数计算返回值。例如,-5 产生返回值 -5,2 + 3 产生返回值 5。少数运算符不产生返回值(如 delete 和 throw),其具体作用将在后续章节阐述。
某些运算符具有额外行为。当运算符(或函数)除返回值外还产生可观察到的效果时,称为具有副作用side effect。例如,x = 5 的副作用是将值 5 赋给变量 x。即使运算符执行完毕,x 的改变值仍可被观察到(例如通过打印 x 的值)。std::cout << 5 的副作用是将 5 打印到控制台。即使 std::cout << 5 执行完毕,我们仍可观察到 5 已被打印到控制台的事实。
术语定义:
在日常语言中,“副作用”一词通常指某件事(如服用药物)发生时产生的次要(往往是负面或意外的)结果。例如,口服抗生素的常见副作用是腹泻。因此,我们常将副作用视为需要避免的事物,或是与主要目标无关的附带现象。在C++中,“副作用”具有不同含义:它指操作符或函数除返回值之外的可观察效应。
由于赋值操作具有改变对象值的可观察效果,因此被视为副作用。我们使用某些运算符(如赋值运算符)主要就是为了其副作用(而非运算符产生的返回值)。在这种情况下,副作用既有益又可预测(而返回值往往只是附带结果)。
对于高级读者:
对于我们主要因其返回值而调用的运算符(如 operator+ 或 operator*),其返回值通常显而易见(例如操作数的和或积)。对于我们主要因其副作用而调用的运算符(如 operator= 或 operator<<),其返回值(若有)则未必清晰。例如,你认为 x = 5 的返回值是什么?
运算符=和运算符<<(用于向控制台输出值时)均返回左操作数。因此 x = 5 返回 x,而 std::cout << 5 返回 std::cout。此设计使这些运算符能够进行链式调用。
例如,x = y = 5 的计算过程为:x = (y = 5)。首先 y = 5 将 5 赋值给 y,该操作返回 y,随后 y 被赋值给 x。
std::cout << “Hello ” << “world!” 的计算过程为:(std::cout << “Hello ”) << “world!”。首先将“Hello ”打印到控制台。该操作返回 std::cout,随后可用于将“world!”同样打印到控制台。
关于运算符求值顺序的详细说明,请参见第6.1节——运算符优先级与结合律。
测验时间
问题 #1
请指出下列各项产生的输出:
a)
std::cout << 3 + 4 << '\n';
显示答案
7
b)
std::cout << 3 + 4 - 5 << '\n';
显示答案
2
c)
std::cout << 2 + 3 * 4 << '\n';
显示答案
14。乘法优先于加法,因此先计算 3 × 4,结果为 12。2 + 12 等于 14。
d) 附加题:
int x { 2 };
std::cout << (x = 5) << '\n';
显示答案
5。x = 5 将值 5 赋给变量 x,然后返回 x。接着将 x 的值(此时为 5)打印到控制台。

浙公网安备 33010602011771号