4.15 - 字面量
Key Takeaway
- 常量有两种,字面量常量(简称字面量)和符号常量
- 字面量的类型具有默认值,可以通过后缀修改
- 小数字面量的默认类型为double而不是float
- C风格的字符串,默认类型为字符数组
std::string
和std::string_view
类型的字面量可以通过s
和sv
后缀指定,在类型推断时比较有用,其他情况一般不需要- 声明浮点数字面量的方法有两种:
3.14159
或1.9e10
- 只有当一个数字(也可能是其他类型)含义不明确或多次被使用时才被看做是魔术数字。有些场合下的字面量,尤其是只使用一次的字面量,可以不被看做是魔术数字
- 使用常量来避免魔术数字
字面量常量(通常称为字面量) 指的是直接嵌入到代码中的未命名的值,例如:
1 2 3 |
|
字面量通常被称为字面量常量,因为你不能动态地改变它们的值
字面量的类型
和其他对象诶新一样,所有的字面量也有其类型。字面量的类型由其值和字面量本身的格式推定。
默认情况下:
字面量值 | 例子 | 默认类型 |
---|---|---|
integral value | 5, 0, -3 | int |
boolean value | true, false | bool |
floating point value | 3.4, -2.2 | double (不是 float)! |
char value | ‘a’ | char |
C-style string | “Hello, world!” | const char[14] |
字面量后缀
如果默认类型不是你想要的,可以通过字面量的后缀改变其类型:
数据类型 | 后缀 | 含义 |
---|---|---|
int | u or U | unsigned int |
int | l or L | long |
int | ul, uL, Ul, UL, lu, lU, Lu, or LU | unsigned long |
int | ll or LL | long long |
int | ull, uLL, Ull, ULL, llu, llU, LLu, or LLU | unsigned long long |
double | f or F | float |
double | l or L | long double |
后缀是区分大小写的,但是由于小写字母L
看起来很像 1 ,所以开发者通常喜欢用大写。
相关内容
我们会在4.17 - std::string 简介 和4.18 - std::string_view 简介中讨论字符串字面量的后缀。
整型字面量
通常情况下,你不需要为整型指定后缀,但是如果要做的话,可以参考下面例子:
1 2 3 4 5 6 7 8 9 |
|
u
(或者 ‘U’) 是一个特例,通常用来标记无符号整型字面量:
1 2 3 4 5 6 7 8 9 |
|
浮点数字面量
默认情况下,浮点数字面量的类型为 double
,如果希望使用 float
类型,则需要指定 f
或F
后缀:
1 2 |
|
新程序员通常会奇怪为什么下面的代码会导致编译告警:
1 |
|
因为,4.1 没有后缀所以是 double
类型的字面量,而不是 float
类型。当C++定义字面量的类型时,它不在乎该字面量的用途(例如,在这个例子中是用于初始化一个 float
变量)。因此,4.1 必须被转换为 double
类型才可以被赋值给变量 f
,而这么做是会导致精度丢失的。
在C++中,只要字面量的意思是清晰的,那么你可以放心使用。例如大多数情况下会使用字面量进行初始化或赋值、数学运算或直接打印到屏幕上。
字符串字面量
在 4.11 - 字符 中我们将字符串定义为一个字符序列的集合。在 C++ 中也支持字符串字面量:
1 2 |
|
出于某些历史原因,C++ 处理字符串的方式有些特别。目前来讲,你可以使用字符串字面量来作为打印文本或用于初始化 std::string
。
扩展阅读
C++ 同样也支持 std::string 和 std::string_view 字面量。虽然在很多时候并不需要使用它们,但是在使用类型推断的时候,它们还是挺有用的。类型推断可以发生在使用 auto
关键字(8.7 - 使用 auto 关键字进行类型推断)或是在类模板参数推断时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
此处使用using
来引入整个命名空间是一个被允许的特例。
我们会在后续的课程中深入讨论字符串字面量。
浮点数字面量的科学计数法表示
声明浮点数字面量的方法有两种:
1 2 |
|
对于科学计数法来讲,指数部分也可以是负值:
1 |
|
魔术数字
魔术数字指的是含义不清或多次使用的字面量(通常是数字)。
下面代码展示了一种含义不清的魔术数字:
1 |
|
这里的 30 到底是什么含义?尽管有时候你可能可以猜到它的含义,例如这里指的是每个教室最多的学生数,但是这个意义并不明显。对于更加复杂的程序来说,推测某个硬编码的数字是很难的,除非有注释。
幸运的是,我们可以使用符号常量来避免这种含义不清的魔术数字:
1 2 |
|
使用魔术数字通常被认为是一种不好的编码习惯,它们不仅没有提供关于其用途的上下文信息,而且还留下了一个隐患(万一需要修改则需要在多处修改)。假设,学校购买了一些新的课桌,现在一个教室能够容纳 35 人了,那么我们的程序也必须反映这一情况。考虑如下代码:
1 2 |
|
为了修改程序适应新的情况,我们必须把常量 30 修改为 35,但是 setMax()
怎么办?它的参数 30 和其他 30 是一个意思吗?应该更新还是应该保留啊,搞不好会导致程序出问题的。如你进行全局的查找和替换,那么你可能会替换掉本不应该被更新的 setMax()
中的 30。所以你可能需要逐个检查每一个 30,确保它应该被替换才执行操作。这就太费时间(而且还容易出错)。
下面的代码 (使用符号常量) 则更加清晰,明显可以看出两个 30 是不是一回事:
1 2 3 4 5 |
|
魔术数字并不总是数字——也可能是字符串或其他类型。
注意,只使用一次,且含义明确的字面量通常不被认为是魔术数字。像-1、0、0.0 和 1这样的值就经常被用在含义非常明确的情境中:
1 2 |
|
其他值在某些语境下含义也是明显的 (因此也可以不被看做是魔术数字):
1 |
|
最佳实践
在代码中避免魔术数字 (使用常量来代替)。