Skip to content

C++ 常量/字面值

字面量(Literals)(通常也被称为常量)是直接嵌入源代码中的固定值。它们表示特定数据,如数字、字符或字符串。

常量(Constants),从更广泛的意义上讲,是指初始化后其值不能更改的标识符(类似于变量)。现代 C++ 提供了几种定义常量的方式。

C++ 支持各种类型的字面量:

表示整数。它们可以用不同的进制指定:

  • 十进制 (base 10): 默认,无前缀(例如 123, -45)。
  • 八进制 (base 8): 前缀 0(例如 0173,即十进制的 123)。避免使用前导零,除非是八进制,因为它可能会引起混淆。
  • 十六进制 (base 16): 前缀 0x 或 0X(例如 0x7B 或 0X7b,两者都是十进制的 123)。使用数字 0-9 和字母 a-f(或 A-F)。
  • 二进制 (base 2) (C++14): 前缀 0b 或 0B(例如 0b01111011,即十进制的 123)。

整型字面量可以带有后缀来指定其类型:

  • u 或 U:unsigned (例如 123u)。
  • l 或 L:long (例如 123L)。
  • ll 或 LL (C++11):long long (例如 123LL)。
  • 可以组合使用(例如 123UL, 45ull)。

C++14 引入了数字分隔符(单引号 ')以提高可读性:

long long big_number = 1'000'000'000LL; // 更易读
int binary_flags = 0b1101'0010'1010'0001;

表示带有小数部分的数字。它们可以用十进制或指数表示法书写:

  • 十进制: 必须包含小数点 (.) 或指数(例如 3.14159, 12.0, .5, 5.)。
  • 指数表示法: 使用 e 或 E,后跟可选的符号和一个整数指数(10 的幂)(例如 6.022e23, 1.6E-19)。
  • 也存在十六进制浮点型字面量(使用 0x 前缀和 p 表示指数),但较不常用(0x1.921fb5p+1)。

后缀指定其类型:

  • 无后缀:double(默认)。
  • f 或 F:float (例如 3.14f)。
  • l 或 L:long double (例如 3.1415926535L)。

也可以使用数字分隔符(')(C++14)。

double pi = 3.141'592'653'589'793;

表示布尔值:

  • true:表示布尔值 true。
  • false:表示布尔值 false。

表示单个字符,用单引号(')括起来。

  • 普通 char: 'a', '?', '7'。类型为 char。
  • 宽字符(Wide Character): L'β'(前缀 L)。类型为 wchar_t。
  • UTF-8 (C++11): u8'貓'(前缀 u8)。类型为 char (C++11/17) 或 char8_t (C++20)。
  • UTF-16 (C++11): u'Ω'(前缀 u)。类型为 char16_t。
  • UTF-32 (C++11): U'🌍'(前缀 U)。类型为 char32_t。

转义序列(Escape Sequences): 使用反斜杠(\)表示的特殊字符:

转义序列含义
\反斜杠 (\) 字符
’单引号 (') 字符
”双引号 (") 字符
?问号 (?) 字符
\n换行符
\t水平制表符
\v垂直制表符
\b退格
\r回车
\f换页
\a响铃
\0空字符
\ooo八进制值(最多 3 位)
\xhh…十六进制值
\uHHHH (C++11)通用字符名称(Unicode - 16 位)
\UHHHHHHHH (C++11)通用字符名称(Unicode - 32 位)

表示字符序列,用双引号(")括起来。它们隐式地以空字符结尾(会自动添加一个 \0 字符)。

  • 普通: "Hello, World!"(类型为 const char[])。
  • 宽字符: L"你好"(前缀 L)。类型为 const wchar_t[]。
  • UTF-8 (C++11): u8"κόσμε"(前缀 u8)。类型为 const char[] (C++11/17) 或 const char8_t[] (C++20)。
  • UTF-16 (C++11): u"नमस्ते"(前缀 u)。类型为 const char16_t[]。
  • UTF-32 (C++11): U"שלום"(前缀 U)。类型为 const char32_t[]。
  • 原始字符串字面量(Raw String Literals)(C++11): R"(No\escape\needed)" 或 R"delimiter(Text)delimiter"。对于包含许多反斜杠或引号的字符串非常有用。

相邻的字符串字面量会被编译器连接起来:

std::cout << "This is part 1, " "and this is part 2.\n";

nullptr (C++11): 表示空指针值。它是类型安全的,在表示空指针时比 0 或 NULL 更推荐使用。

常量是初始化后其值不能更改的命名实体。现代 C++ 提供了两种主要方式:

const 限定符将对象声明为不可变的。其值必须在声明时初始化,并且之后不能修改。const 变量具有与普通变量相同的作

#include <iostream>
int main() {
const int MAX_USERS = 100;
const double PI = 3.14159;
const char NEWLINE = '\n';
// 错误!不能赋值给 const 变量。
// MAX_USERS = 200;
// 使用常量
double circumference = 2 * PI * 5.0;
std::cout << "Max users: " << MAX_USERS << NEWLINE;
std::cout << "Circumference: " << circumference << NEWLINE;
return 0;
}

相比于 #define,使用 const 定义常量更受推荐,因为它是类型安全的并且遵循作用域规则。

constexpr 声明一个表达式 可以 在编译时求值。当应用于变量时,它隐含了 const 属性,并要求初始化表达式是一个编译时常量表达式。

constexpr 允许在需要编译时求值的上下文中使用常量,例如数组边界(在 C++11 VLAs 之前)、模板参数和枚举初始化器。

#include <array> // 用于 std::array
constexpr int get_array_size() {
return 5 * 2;
}
int main() {
constexpr int COMPILED_SIZE = 10;
constexpr double GRAVITY = 9.80665;
constexpr int DYNAMIC_SIZE = get_array_size(); // 在编译时求值
int myArray[COMPILED_SIZE]; // OK: COMPILED_SIZE 是编译时常量
std::array<double, DYNAMIC_SIZE> stdArray; // OK: DYNAMIC_SIZE 是编译时常量
// 错误!constexpr 隐含了 const
// GRAVITY = 9.9;
return 0;
}

最佳实践: 对于可以在编译时求值的常量,优先使用 constexpr。对于在运行时初始化但之后不应更改的值,使用 const。

预处理器指令 #define 也可以为值创建符号名称(宏)。

#define MAX_ITEMS 10 // 旧的 C 风格,不建议用于常量

然而,在现代 C++ 中通常不鼓励使用 #define 定义常量,原因如下:

  • 它缺乏类型信息(只是简单的文本替换)。
  • 它不遵循作用域规则(宏在定义后在整个翻译单元中是全局的)。
  • 由于宏展开规则,它可能导致意外的行为。
  • 它不容易调试(调试器看到的是替换后的值,而不是宏名称)。

因此,请使用 const 或 constexpr 来代替,以获得类型安全和更好的代码结构。