常量

常量 是值固定的表达式。

字面值

字面值是最明显的常量类型。它们用于在程序的源代码中表达特定值。我们在之前的章节中已经使用了一些字面值,用于给变量赋值或表达我们希望程序打印出来的消息,例如,当我们写下

1
a = 5;

这段代码中的 5 是一个 字面值常量

字面值常量可以分为:整数、浮点数、字符、字符串、布尔值、指针和用户定义字面值。

整数数值


1
2
3
1776
707
-

这些是表示整数值的数值常量。请注意,它们不包含在引号或其他特殊字符中;它们是数字的简单序列,代表十进制表示的整数;例如,1776 始终代表值 一千七百七十六

除了十进制数(我们大多数人每天使用的数字)之外,C++ 还允许使用八进制数(基数为 8)和十六进制数(基数为 16)作为字面值常量。对于八进制字面值,数字前会加上一个 0(零)字符。对于十六进制,它们的前面是 0x(零,x)字符。例如,以下字面值常量都等价于

1
2
3
75         // decimal
0113       // octal
0x4b       // hexadecimal  

所有这些都代表相同的数字:75(七十五),分别表示为十进制、八进制和十六进制数值。

这些字面值常量就像变量一样,也有类型。默认情况下,整数字面值是 int 类型。但是,可以在整数字面值后附加某些后缀来指定不同的整数类型

后缀类型修饰符
u Uunsigned
l Llong
ll LLlong long

unsigned 可以与另外两个中的任何一个以任何顺序组合,形成 unsigned longunsigned long long

例如:

1
2
3
4
5
75         // int
75u        // unsigned int
75l        // long
75ul       // unsigned long 
75lu       // unsigned long 

在以上所有情况下,后缀都可以使用大写或小写字母指定。

浮点数数值

它们表示实数值,带有小数和/或指数。它们可以包含小数点、e 字符(表示“乘以 10 的 X 次方”,其中 X 是跟在 e 字符后面的整数值),或者同时包含小数点和 e 字符。

1
2
3
4
3.14159    // 3.14159
6.02e23    // 6.02 x 10^23
1.6e-19    // 1.6 x 10^-19
3.0        // 3.0  

这四个是 C++ 中有效的带小数的数字。第一个数字是 PI,第二个是阿伏伽德罗数,第三个是电子的电荷(一个极小的数)- 所有这些都是近似值 -,最后一个数字是 ,表示为浮点数字面值。

浮点数字面值的默认类型是 double。可以通过添加以下后缀之一来指定 floatlong double 类型的浮点数字面值

后缀类型
f Ffloat
l Llong double

例如:

1
2
3.14159L   // long double
6.02e23f   // float  

浮点数字面常量中可能用到的字母(efl)中的任何一个都可以使用小写或大写字母编写,含义没有区别。

字符和字符串字面值

字符和字符串字面值包含在引号中。

1
2
3
4
'z'
'p'
"Hello world"
"How do you do?"

前两个表达式表示单字符字面值,后两个表示由多个字符组成的字符串字面值。请注意,要表示单个字符,我们将其放在单引号(')之间;要表示字符串(通常由多个字符组成),我们将字符放在双引号(")之间。

单字符和字符串字面值都需要引号包围,以将它们与可能的变量标识符或保留关键字区分开。请注意这两个表达式之间的区别

x
'x'

在这里,单独的 x 指的是一个标识符,例如变量名或复合类型名;而 'x'(包含在单引号中)指的是字符字面值 'x'(代表小写字母 x 的字符)。

字符和字符串字面值还可以表示难以或不可能在程序源代码中以其他方式表达的特殊字符,如换行符(\n)或制表符(\t)。所有这些特殊字符前面都带有反斜杠字符(\)。

这里是单字符转义码的列表

转义码描述
\n换行
\r回车
\t制表符
\v垂直制表符
\b退格
\f换页符
\a响铃(蜂鸣)
\'单引号('
\"双引号("
\?问号(?
\\反斜杠(\

例如:

'\n'
'\t'
"Left \t Right"
"one\ntwo\nthree"

在内部,计算机将字符表示为数字代码:最典型的是,它们使用 ASCII 字符编码系统的一种扩展(有关更多信息,请参阅 ASCII 码)。字符也可以在字面值中使用其数字代码表示,方法是写入一个反斜杠字符(\),后跟以八进制(基数为 8)或十六进制(基数为 16)表示的代码。对于八进制值,反斜杠后面直接跟着数字;而对于十六进制,则在反斜杠和十六进制数字本身之间插入一个 x 字符(例如:\x20\x4A)。

可以通过将多个字符串字面值用一个或多个空白字符(包括制表符、换行符和其他有效空白字符)分隔来将它们连接成一个字符串字面值。例如

1
2
"this forms" "a single"     " string "
"of characters"

上面的字符串字面值等价于

1
"this formsa single string of characters"

请注意,引号内的空格是字面值的一部分,而引号外的空格则不是。

一些程序员还使用一个技巧来包含多行长字符串字面值:在 C++ 中,行尾的反斜杠(\)被视为一个行续接字符,它将当前行和下一行合并为一行。因此,以下代码

1
2
x = "string expressed in \
two lines"

等价于

1
x = "string expressed in two lines"

以上所有字符字面值和字符串字面值都由 char 类型的字符组成。可以通过使用以下任一前缀来指定不同的字符类型

前缀字符类型
uchar16_t
Uchar32_t
Lwchar_t

请注意,与整数字面值的类型后缀不同,这些前缀是区分大小写的char16_t 使用小写,而 char32_twchar_t 使用大写。

对于字符串字面值,除了上述的 uUL 之外,还有两个额外的前缀

前缀描述
u8字符串字面值在可执行文件中使用 UTF-8 编码
R字符串字面值是原始字符串

在原始字符串中,反斜杠、单引号和双引号都是有效字符;字面值的内容由一个起始的 R"sequence(" 和一个结束的 ")sequence" 分隔,其中 sequence 是任何字符序列(包括空序列)。字符串的内容是括号内的内容,忽略分隔序列本身。例如

1
2
R"(string with \backslash)"
R"&%$(string with \backslash)&%$"

以上两个字符串都等价于 "string with \\backslash"R 前缀可以与任何其他前缀结合使用,例如 uLu8

其他字面值

C++ 中存在三个关键字字面值:truefalsenullptr
  • truefalsebool 类型变量的两个可能值。
  • nullptr空指针值。

1
2
3
bool foo = true;
bool bar = false;
int* p = nullptr;

类型化常量表达式

有时,为常量值命名是很方便的。

1
2
const double pi = 3.1415926;
const char tab = '\t';

然后,我们可以使用这些名称来代替它们被定义的字面值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;

const double pi = 3.14159;
const char newline = '\n';

int main ()
{
  double r=5.0;               // radius
  double circle;

  circle = 2 * pi * r;
  cout << circle;
  cout << newline;
}
31.4159

预处理器定义 (#define)

命名常量值的另一种机制是使用预处理器定义。它们的格式如下

#define 标识符 替换内容

在此指令之后,代码中 identifier 的任何出现都将被解释为 replacement,其中 replacement 是任何字符序列(直到行尾)。此替换由预处理器执行,并在程序编译之前发生,从而导致一种盲目替换:涉及的类型或语法的有效性不会以任何方式进行检查。

例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;

#define PI 3.14159
#define NEWLINE '\n'

int main ()
{
  double r=5.0;               // radius
  double circle;

  circle = 2 * PI * r;
  cout << circle;
  cout << NEWLINE;

}
31.4159

请注意,#define 行是预处理器指令,因此它们是单行指令,与 C++ 语句不同,不需要在末尾加分号(;);指令会自动扩展到行尾。如果行中包含分号,它将成为替换序列的一部分,并且也会包含在所有被替换的出现中。
Index
目录