String literals
构造一个指定字符数组类型的无名对象(就地构造),当需要在源代码中嵌入字符串时使用。
目录 |
语法
"
s-char-sequence
"
|
(1) | ||||||||
u8"
s-char-sequence
"
|
(2) | (自 C11 起) | |||||||
u"
s-char-sequence
"
|
(3) | (自 C11 起) | |||||||
U"
s-char-sequence
"
|
(4) | (自 C11 起) | |||||||
L"
s-char-sequence
"
|
(5) | ||||||||
其中
| s-char-sequence | - |
零个或多个字符,其中每个字符要么是源字符集中的多字节字符(不包括(
"
)、
\
和换行符),要么是按
转义序列
定义的字符转义、十六进制转义、八进制转义
,或通用字符名
(C99起)
。
|
N
是以执行窄编码代码单元表示的字符串大小(包含空终止符)。数组中的每个
char
元素均使用执行字符集从
s-char-sequence
中的下一个字符进行初始化。
N
是包含空终止符的 UTF-8 代码单元字符串大小。数组中的每个
char
(C23 前)
char8_t
(C23 起)
元素均使用 UTF-8 编码从
s-char-sequence
中的下一个多字节字符初始化。
|
3)
16位宽字符串字面量:该字面量的类型为
char16_t
[
N
]
,其中
N
是以实现定义的16位编码(通常为UTF-16)的码元表示的字符串大小,包含空终止符。数组中的每个
char16_t
元素会如同通过在执行环境中以实现定义的区域设置调用
mbrtoc16
进行初始化。
4)
32位宽字符串字面量:该字面量的类型为
char32_t
[
N
]
,其中
N
是以实现定义的32位编码(通常为UTF-32)的码元表示的字符串大小,包含空终止符。数组中的每个
char32_t
元素会如同通过在执行环境中以实现定义的区域设置调用
mbrtoc32
进行初始化。
|
(C23前) |
|
3)
UTF-16字符串字面量
:该字面量的类型为
char16_t
[
N
]
,其中
N
是以UTF-16码元表示的字符串大小,包含空终止符。数组中的每个
char16_t
元素会使用UTF-16编码从
s-char-sequence
中的下一个多字节字符进行初始化。
4)
UTF-32字符串字面量
:该字面量的类型为
char32_t
[
N
]
,其中
N
是以UTF-32码元表示的字符串大小,包含空终止符。数组中的每个
char32_t
元素会使用UTF-32编码从
s-char-sequence
中的下一个多字节字符进行初始化。
|
(C23起) |
N
是执行宽编码中字符串的代码单元数量(包含空终止符)。数组中的每个
wchar_t
元素都会以在实现定义的区域设置中执行
mbstowcs
的方式进行初始化。
说明
首先,在 翻译阶段6 (宏展开之后),相邻的字符串字面量(即仅由空白字符分隔的字符串字面量)会被连接起来。
|
只能连接两个窄字符串字面量或两个宽字符串字面量。 |
(C99 前) |
|
若一个字面量无前缀,则结果字符串字面量的宽度/编码由带前缀的字面量指定。 L"Δx = %" PRId16 // 在阶段 4,PRId16 扩展为 "d" // 在阶段 6,L"Δx = %" 和 "d" 形成 L"Δx = %d" |
(C99 起) |
|
若两个字符串字面量拥有不同的编码前缀,则连接操作由实现定义,但 UTF-8 字符串字面量与宽字符串字面量不可连接。 |
(since C11)
(until C23) |
|
若两个字符串字面量拥有不同的编码前缀,则连接操作非良构。 |
(since C23) |
其次,在 翻译阶段7 中,每个字符串字面量都会添加一个终止空字符,然后每个字面量会初始化一个具有静态 存储期 的无名数组,该数组的长度恰好足以包含字符串字面量的内容加上用于空终止符的一个额外字符。
char* p = "\x12" "3"; // 创建一个静态 char[3] 数组,其内容为 {'\x12', '3', '\0'} // 将 p 指向该数组的首元素
字符串字面量
不可修改
(实际上可能被置于只读内存区域如
.rodata
)。若程序尝试修改由字符串字面量构成的静态数组,其行为是未定义的。
char* p = "Hello"; p[1] = 'M'; // 未定义行为 char a[] = "Hello"; a[1] = 'M'; // 正确:a 不是字符串字面量
相同的字符串字面量是否引用内存中的同一位置,既非必需也非禁止。此外,重叠的字符串字面量或作为其他字符串字面量子串的字符串字面量可能会被合并。
"def" == 3+"abcdef"; // 结果可能是1或0,由具体实现定义
注释
字符串字面量不一定是字符串;如果字符串字面量包含嵌入的空字符,它表示包含多个字符串的数组:
char* p = "abc\0def"; // strlen(p) == 3,但该数组的大小为8
如果在字符串字面值中十六进制转义符后紧跟一个有效的十六进制数字,会导致编译失败(无效转义序列),但可以通过字符串连接作为解决方案:
//char* p = "\xfff"; // 错误:十六进制转义序列超出范围 char* p = "\xff""f"; // 正确,该字面量为 char[3] 类型,包含 {'\xff', 'f', '\0'}
字符串字面量可用于 初始化数组 ,若数组大小比字符串字面量长度小一,则空终止符将被忽略:
char a1[] = "abc"; // a1 是 char[4] 包含 {'a', 'b', 'c', '\0'} char a2[4] = "abc"; // a2 是 char[4] 包含 {'a', 'b', 'c', '\0'} char a3[3] = "abc"; // a3 是 char[3] 包含 {'a', 'b', 'c'}
字符串字面量 (1) 与宽字符串字面量 (5) 的编码方式由具体实现定义。例如,gcc 通过 命令行选项 - fexec - charset 和 - fwide - exec - charset 来设定这些编码。
尽管C11标准允许混合宽字符串字面量连接,但几乎所有编译器都拒绝此类连接(目前已知的唯一例外是 SDCC ),且其使用经验尚不明确。因此,C23标准中移除了对混合宽字符串字面量连接的允许。
示例
#include <inttypes.h> #include <locale.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <uchar.h> int main(void) { char s1[] = "a猫🍌"; // 或 "a\u732B\U0001F34C" #if __STDC_VERSION__ >= 202311L char8_t #else char #endif s2[] = u8"a猫🍌"; char16_t s3[] = u"a猫🍌"; char32_t s4[] = U"a猫🍌"; wchar_t s5[] = L"a猫🍌"; setlocale(LC_ALL, "en_US.utf8"); printf(" \"%s\" is a char[%zu] holding { ", s1, sizeof s1 / sizeof *s1); for(size_t n = 0; n < sizeof s1 / sizeof *s1; ++n) printf("0x%02X ", +(unsigned char)s1[n]); puts("}"); printf( #if __STDC_VERSION__ >= 202311L "u8\"%s\" is a char8_t[%zu] holding { " #else "u8\"%s\" is a char[%zu] holding { " #endif , s2, sizeof s2 / sizeof *s2); for(size_t n = 0; n < sizeof s2 / sizeof *s2; ++n) #if __STDC_VERSION__ >= 202311L printf("0x%02X ", s2[n]); #else printf("0x%02X ", +(unsigned char)s2[n]); #endif puts("}"); printf(" u\"a猫🍌\" is a char16_t[%zu] holding { ", sizeof s3 / sizeof *s3); for(size_t n = 0; n < sizeof s3 / sizeof *s3; ++n) printf("0x%04" PRIXLEAST16" ", s3[n]); puts("}"); printf(" U\"a猫🍌\" is a char32_t[%zu] holding { ", sizeof s4 / sizeof *s4); for(size_t n = 0; n < sizeof s4 / sizeof *s4; ++n) printf("0x%08" PRIXLEAST32" ", s4[n]); puts("}"); printf(" L\"%ls\" is a wchar_t[%zu] holding { ", s5, sizeof s5 / sizeof *s5); for(size_t n = 0; n < sizeof s5 / sizeof *s5; ++n) printf("0x%08X ", (unsigned)s5[n]); puts("}"); }
可能的输出:
"a猫🍌" is a char[9] holding { 0x61 0xE7 0x8C 0xAB 0xF0 0x9F 0x8D 0x8C 0x00 }
u8"a猫🍌" is a char[9] holding { 0x61 0xE7 0x8C 0xAB 0xF0 0x9F 0x8D 0x8C 0x00 }
u"a猫🍌" is a char16_t[5] holding { 0x0061 0x732B 0xD83C 0xDF4C 0x0000 }
U"a猫🍌" is a char32_t[4] holding { 0x00000061 0x0000732B 0x0001F34C 0x00000000 }
L"a猫🍌" is a wchar_t[4] holding { 0x00000061 0x0000732B 0x0001F34C 0x00000000 }
参考文献
- C23 标准 (ISO/IEC 9899:2024):
-
- 6.4.5 字符串字面量 (p: TBD)
- C17 标准 (ISO/IEC 9899:2018):
-
- 6.4.5 字符串字面量 (p: 50-52)
- C11 标准 (ISO/IEC 9899:2011):
-
- 6.4.5 字符串字面量 (p: 70-72)
- C99标准(ISO/IEC 9899:1999):
-
- 6.4.5 字符串字面量(页码:62-63)
- C89/C90 标准 (ISO/IEC 9899:1990):
-
- 3.1.4 字符串字面量
参见
|
C++ 文档
关于
string literal
|