Cpp学习7 第 5 章 常量与字符串
5.1 — 常量变量(命名常量)
在编程中, 常量是指在程序执行期间不能改变的值。
命名常量 Named constants是与标识符关联的常量值。它们有时也称为符号常量 symbolic constants 。
字面常量 Literal constants是指不与标识符关联的常量值。
在 C++ 中,定义命名常量有三种方法:
- 常量变量(本课将介绍)。
- 带有替换文本的对象型宏(在第 2.10 课—预处理器简介中介绍,本课将进行补充介绍)。
- 枚举常量(在第 13.2 课中介绍—非作用域枚举 )。
Constant variables 常量变量
1 | const double gravity { 9.8 }; // preferred use of const before type |
常量变量 必须 在定义时进行初始化,之后不能通过赋值来改变其值, const 量可以从其他变量(包括非常量变量)初始化:
由于常量变量的行为与普通变量类似(只是不能赋值),因此没有必要采用特殊的命名规则。正因如此,我们更倾向于使用与非常量变量相同的命名规则(例如 earthGravity )。
不要将 const 用于函数形式参数(没啥用,冗余)。
不要将 const 用于函数返回类型(没啥用,冗余)。
如果一个变量可以设为常量,通常应该将其设为常量。
带有替换文本的对象式宏
1 |
|
当预处理器处理包含此代码的文件时,它会将第 7 行的 MY_NAME 替换为 "Alex" 。请注意, MY_NAME 是一个名称,而替换文本是一个常量值,因此带有替换文本的对象型宏也是命名常量。
优先使用常量变量,而不是带有替换文本的对象型宏。
在多文件程序中使用常量变量,见7.10课
命名规则:类型限定符 type qualifiers
类型限定符 (有时简称限定符 )是一个关键字,它应用于类型,用于修改该类型的行为。用于声明常量变量的 const 称为 const 类型限定符 (或简称 const 限定符 )。
从 C++23 开始,C++ 只有两个类型限定符: const 和 volatile 。volatile 限定符用于告诉编译器,一个对象的值可能随时被更改。这个很少使用的限定符会禁用某些类型的优化。
5.2 — 字面量 Literals
字面量是直接插入到代码中的值。
如果字面量的默认类型不符合预期,可以通过添加后缀来更改字面量的类型。
默认情况下,浮点字面量的类型为 double 。要将其改为 float 字面量,应使用 f (或 F )后缀。浮点字面量还可以用科学计数法表示。
字符串字面量
所有 C 风格的字符串字面量都隐含一个空终止符。例如,考虑字符串 "hello" 。虽然这个 C 风格字符串看起来只有五个字符,但实际上它有六个: 'h' 、 'e' 、 'l ‘、 'l' 、 'o' 和 '\0' (ASCII 码为 0 的字符)。末尾的 ‘\0’ 字符是一个特殊字符,称为空终止符 ,用于指示字符串的结尾。以空终止符结尾的字符串称为空终止字符串 。
与其他大多数字面量(它们是值,而不是对象)不同,C 风格的字符串字面量是常量对象,它们在程序开始时创建,并保证在整个程序运行期间都存在。这一点在几节课中讨论 std::string_view 时会变得很重要。
与 C 风格的字符串字面量不同, std::string 和 std::string_view 字面量会创建临时对象。这些临时对象必须立即使用,因为它们会在创建它们的完整表达式结束后被销毁。
魔法数字
魔法数字是指含义不明确或可能需要在以后更改的字面值(通常是一个数字)。
在复杂的程序中,如果没有注释进行解释,就很难推断字面量代表什么。
避免在代码中使用魔法数字(改用 constexpr 变量,参见第 5.6 课 — constexpr 变量 )。
5.3 — 数制(十进制、二进制、十六进制和八进制)
要使用八进制字面量,请在字面量前面加上 0。例如int x{012}
要使用十六进制字面量,请在字面量前加上 0x 前缀。例如int x{0xF}。
从 C++14 开始,我们可以使用 0b 前缀来使用二进制字面量。
由于过长的字面量难以阅读,C++14 还增加了使用引号 (‘) 作为数字分隔符的功能。
1 |
|
另请注意,分隔符不能出现在值的第一位数字之前。数字分隔符纯粹是视觉上的,不会以任何方式影响字面值。
默认情况下,C++ 以十进制输出值。但是,您可以使用 std::dec 、 std::oct 和 std::hex I/O 操作符来更改输出格式。这种修改是 sticky 的。
以二进制形式输出值稍微复杂一些,因为 std::cout 本身并不具备这种功能。幸运的是,C++ 标准库中包含一个名为 std::bitset 类型,它可以帮我们实现这个功能(在头文件中)。略。
5.4 — as-if 规则和编译时优化
在编程中, 优化是指修改软件以使其更高效地运行(例如,运行速度更快或资源占用更少)的过程。优化可以对应用程序的整体性能水平产生巨大影响。
某些类型的优化通常需要手动完成。可以使用名为性能分析器 profiler 的程序来查看程序各个部分的运行时间,以及哪些部分影响了整体性能。然后,程序员可以寻找方法来缓解这些性能问题。由于手动优化速度较慢,程序员通常专注于进行能够产生显著影响的高级改进(例如选择性能更高的算法、优化数据存储和访问、降低资源占用、并行化任务等)。
其他类型的优化可以自动执行,使用 优化器 optimizer。
“as-if 规则” 规定,只要不影响程序的“可观察行为”,编译器可以随意修改程序以生成更优化的代码。
现代 C++ 编译器能够在编译时(而不是运行时)对某些表达式进行全部或部分求值。当编译器在编译时对表达式进行全部或部分求值时,这被称为编译时求值 。
常量折叠、常量传播;死代码消除….
使用常量变量可以帮助编译器更有效地进行优化。
5.5 — 常量表达式
C++ 语言提供了一些方法,允许我们在编译时明确指定要执行的代码部分。使用能够实现编译时求值的语言特性被称为编译时编程 。
编译时优化(被动):编译器说了算。 你甚至不需要写任何特殊代码,只要打开优化开关(如
-O2),编译器就会自己分析代码,把能提前算的算出来。它想优化就优化,不想优化就不优化,你管不着。编译时编程(主动):你(程序员)说了算。 你必须主动使用
constexpr、consteval等关键词。这等于给编译器下了一道死命令:“这段你必须编译时给我算出来,算不出来就报错给我看,别编译通过!”
常量表达式也是一个由字面量、常量变量、运算符和函数调用组成的非空序列,其中所有元素都必须在编译时可求值。
略。
5.6 — Constexpr 变量
在变量声明中使用 constexpr 关键字(它是“常量表达式”的简写)而不是 const 变量,确保在需要的地方获得编译时常量变量。constexpr 变量必须使用常量表达式进行初始化,否则会导致编译错误。
const 和 constexpr
任何初始化器为常量表达式的常量变量都应该声明为 constexpr 。
任何初始化器不是常量表达式的常量变量(使其成为运行时常量)都应该声明为 const 。
const不一定会编译时求值,而constexpr一定会。
constexpr 函数是一种可以在常量表达式中调用的函数。当 constexpr 函数所属的常量表达式必须在编译时求值时(例如在 constexpr 变量的初始化器中),constexpr 函数必须在编译时求值。否则,constexpr 函数可以在编译时(如果符合条件)或运行时求值。要符合编译时执行的条件,所有参数都必须是常量表达式。可以用来写复杂的预处理函数,真的是数学上的函数那种。
“数据在编译时能确定,且它后面用于定大小、选分支、做断言,用 constexpr!”
5.7 — std::string 简介
虽然可以使用 C 风格的字符串字面量,但 C 风格的字符串变量行为怪异,难以操作(例如,不能使用赋值语句给 C 风格的字符串变量赋新值),而且存在风险(例如,如果将一个较长的 C 风格字符串复制到分配给较短 C 风格字符串的空间中,会导致未定义行为)。在现代 C++ 中,最好避免使用 C 风格的字符串变量。
C++ 引入了两种更易于使用且更安全的字符串类型: std::string 和 std::string_view (C++17)。与我们之前介绍的类型不同, std::string 和 std::string_view 并非基本类型(它们是类,我们将在后续章节中介绍)。
std::string
在 C++ 中,处理字符串和字符串对象的最简单方法是通过 std::string 类型,该类型位于头文件<string>中。
1 |
|
std::string 最巧妙的功能之一就是可以存储不同长度的字符串。如果 std::string 没有足够的内存来存储字符串,它会在运行时使用一种称为 动态内存分配 的内存分配方式来请求额外的内存。这种获取额外内存的能力是 std::string 如此灵活的原因之一,但也使其速度相对较慢。
要将整行输入读取到字符串中,最好使用 std::getline() 函数。
1 |
|
std::ws 是 输入操纵符,指示 std::cin 在提取数据之前忽略任何前导空白字符。前导空白字符是指字符串开头出现的任何空白字符(空格、制表符、换行符)。
如果使用 std::getline() 读取字符串,请使用 std::cin >> std::ws 输入操纵符来忽略前导空格。每次调用 std::getline() 时都需要这样做,因为 std::ws` 在调用之间不会保留。
用 name.length() 查询长度。
另请注意, std::string::length() 返回的是一个无符号整数值(很可能是 size_t 类型)。如果要将长度赋值给一个 int 变量,则应该使用 static_cast 其转换为 int 类型,以避免编译器发出关于有符号/无符号转换的警告。
每次初始化 std::string 时,都会复制用于初始化的字符串。复制字符串的开销很大,因此应尽量减少复制次数。不要按值传递 std::string ,因为它会进行代价高昂的复制。
大多数情况下,请使用 std::string_view 参数
std::string 的字面量
双引号字符串字面量(如“Hello, world!”)默认是 C 风格的字符串。
通过在双引号括起来的字符串字面量后添加后缀 s 来创建类型为 std::string 字符串字面量。
1 |
|
访问字面量后缀的最简洁方法是通过 using 指令 using namespace std::literals 。但是,这会将 所有 标准库字面量导入到 using 指令的作用域中,从而引入很多你可能用不到的东西。
我们建议 using namespace std::string_literals ,它只导入 std::string 的字面量。
可能不需要经常使用 std::string 字面量(因为用 C 风格的字符串字面量初始化 std::string 对象是可以的),但我们将在以后的课程中看到一些情况(涉及类型推导),在这些情况下,使用 std::string 字面量而不是 C 风格的字符串字面量会使事情变得更容易。
constexpr std::string 在 C++17 或更早版本中完全不受支持,在 C++20/23 中也仅在非常有限的情况下有效。如果您需要 constexpr 字符串,请改用 std::string_view
quiz
1 | void printResult(std::string name, int age) { |
5.8 — std::string_view 简介
为了解决 std::string 初始化(或复制)开销过大的问题,C++17 引入了 std::string_view (位于头文件中<string_view>)。std std::string_view 允许对 现有 字符串(C 风格字符串、 std::string 或其他 std::string_view )进行只读访问,而无需创建副本。 只读意味着我们可以访问和使用被查看的值,但不能对其进行修改。
1 |
|
std::string_view 的字面量,略。
与 std::string 不同, std::string_view 完全支持 constexpr:
5.9 — std::string_view(第 2 部分)
std::string 是(唯一)所有者。字符串复制成本很高,为什么要为不需要的副本付费呢?std::string_view 是一个查看器。
view 依赖于被查看的对象。如果在 view 仍在运行时被查看的对象被修改或销毁,则会导致意外或未定义的行为。 a dangling view
std::string_view 最好用作只读函数参数。
std::string_view or const std::string& function parameters ?
大多数情况下,最好使用 std::string_view 。
不要使用std::string 初始化 std::string_view,因为这会导致 std::string_view 悬空。 a C-style string literal or a std::string_view literal 是可以的。
view 修改功能
注意不要编写任何假定 std::string_view 以空字符结尾的代码。
关于 std::string 需要记住的事项:
- 初始化和复制
std::string开销很大,所以应尽可能避免这样做。 - 避免按值传递
std::string,因为这会创建一个副本。 - 尽量避免创建生命周期很短的
std::string对象。 - 修改
std::string将使对该字符串的所有视图失效。 - 按值返回本地
std::string是可以的。
关于 std::string_view 需要记住的事项:
std::string_view通常用于传递字符串函数参数并返回字符串字面量。- 因为整个程序都存在 C 风格的字符串字面量,所以始终可以将
std::string_view设置为 C 风格的字符串字面量。 - 当一个字符串被销毁时,所有指向该字符串的视图都会失效。
- 使用已失效的视图(除了使用赋值重新验证视图之外)会导致未定义行为。
std::string_view可能以空字符结尾,也可能不以空字符结尾。
5.x 测验
1 |
|
- 标题: Cpp学习7 第 5 章 常量与字符串
- 作者: 铁名_IronName
- 创建于 : 2026-08-08 09:43:32
- 更新于 : 2026-08-08 13:38:56
- 链接: https://blog.ironname.top/2026/Cpp/Cpp学习7/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。