在线文档教程
C
C 语法

string literals

字符串文字

构造一个指定字符数组类型的未命名对象,用于需要在源代码中嵌入字符串时使用。

句法

" s-char-sequence "(1)
u8 " s-char-sequence "(2)(since C11)
u " s-char-sequence "(3)(since C11)
U " s-char-sequence "(4)(since C11)
L " s-char-sequence "(5)

其中

S-炭序列-零个或多个字符,每个字符可以是源字符集中的多字节字符(不包括(“),\和换行符),也可以是字符转义,十六进制转义,八进制转义或统一字符名称(自C99开始)在转义序列中。

1)字符串文字文字的类型是char[],数组中的每个字符都是使用执行字符集从s-char-sequence中的下一个字符初始化的。

2)UTF-8字符串文字文字的类型是char[],数组中的每个字符都是使用UTF-8编码从s-char-sequence中的下一个多字节字符初始化的。

3)16位宽字符串文字:文字的类型是char16_t[],数组中的每个char16_t元素都被初始化,就好像通过mbrtoc16在实现定义的区域设置中执行一样。

4)32位宽的字符串文字:文字的类型是char32_t[],数组中的每个char32_t元素都被初始化,就好像通过mbrtoc32在实现定义的区域设置中执行一样。

5)宽字符串文字:文字的类型是wchar_t[],数组中的每个wchar_t元素被初始化,就好像通过mbstowcs在实现定义的区域设置中执行一样。

说明

首先,在翻译阶段6(宏扩展之后),将相邻的字符串文字(即仅由空白分隔的字符串文字)连接起来。

只有两个窄或两个宽字符串可以连接在一起。(直到C99)
如果一个文字没有前缀,那么结果字符串文字的宽度/编码由前缀文字指定。如果两个字符串文字具有不同的编码前缀,则连接是实现定义的。L“Δx=%”PRId16 //在阶段4,PRId16在阶段6扩展为“d”// L“Δx=%”和“d”形成L“Δx=%d”(自C99以来)

其次,在翻译阶段7,将终止空字符添加到每个字符串文字中,然后每个文字初始化一个静态存储持续时间和长度的未命名数组,以便足以容纳字符串文字的内容加上一个空终止符。

char* p = "\x12" "3"; // creates a static char[3] array holding {'\x12', '3', '\0'} // sets p to point to the first element of the array

字符串文字是不可修改的(事实上​​可以放在只读存储器中.rodata)。如果程序试图修改由字符串文字形成的静态数组,则行为是不确定的。

char* p = "Hello"; p[1] = 'M'; // Undefined behavior

对于相同的字符串文字来说,既不要求也不禁止在存储器中引用相同的位置。而且,重叠的字符串文字或者是其他字符串文字的子字符串的字符串文字可以组合。

"def" == 3+"abcdef"; // may be 1 or 0, implementation-defined

注意

字符串文字不一定是一个字符串; 如果字符串文字含有空字符,则表示包含多个字符串的数组:

char* p = "abc\0def"; // strlen(p) == 3, but the array has size 8

如果一个有效的十六进制数字在字符串文字中出现十六进制转义,它将无法编译为无效转义序列,但字符串连接可以用作解决方法:

//char* p = "\xfff"; // error: hex escape sequence out of range char* p = "\xff""f"; // okay, the literal is char[3] holding {'\xff', 'f', '\0'}

字符串文字可用于初始化数组,如果数组大小小于字符串文字的大小,则会忽略空终止符:

char a1[] = "abc"; // a1 is char[4] holding {'a', 'b', 'c', '\0'} char a2[4] = "abc"; // a2 is char[4] holding {'a', 'b', 'c', '\0'} char a3[3] = "abc"; // a3 is char[3] holding {'a', 'b', 'c'}

字符串文字(1)和宽字符串文字(5)的编码是实现定义的。例如,GCC与选择它们的命令行选项 -fexec-charset-fwide-exec-charset

#include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <uchar.h> #include <locale.h> int main(void) { char s1[] = "a猫?"; // or "a\u732B\U0001F34C" char 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("%#x ", +(unsigned char)s1[n] puts(" }" printf("u8\"%s\" is a char[%zu] holding { ", s2, sizeof s2 / sizeof *s2 for(size_t n = 0; n < sizeof s2 / sizeof *s2; ++n) printf("%#x ", +(unsigned char)s2[n] 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("%#x ", 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("%#x ", 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("%#x ", s5[n] puts(" }" }

可能的输出:

"a猫?" is a char[9] holding { 0x61 0xe7 0x8c 0xab 0xf0 0x9f 0x8d 0x8c 0 } u8"a猫?" is a char[9] holding { 0x61 0xe7 0x8c 0xab 0xf0 0x9f 0x8d 0x8c 0 } u"a猫?" is a char16_t[5] holding { 0x61 0x732b 0xd83c 0xdf4c 0 } U"a猫?" is a char32_t[4] holding { 0x61 0x732b 0x1f34c 0 } L"a猫?" is a wchar_t[4] holding { 0x61 0x732b 0x1f34c 0 }