字符串字面量

< c‎ | language

原位构造指定字符数组类型的无名对象,在需要内嵌字符串到源代码中时使用。

语法

" 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 起)


1) 字符串字面量:字面量的类型为 char[N] ,其中 N 是以执行窄编码的编码单元数计的字符串的大小,包括空终止符。以执行字符集从 s-char-sequence 中的下个字符初始化数组的每个 char 元素。
2) UTF-8 字符串字面量:字面量的类型为 char[N] ,其中 N 是以 UTF-8 编码单元数计的字符串大小,包括空终止符。以 UTF-8 编码从 s-char-sequence 中的下个多字节字符初始化数组中的每个 char 元素。
3) 16 位宽字符串字面量:字面量的类型为 char16_t[N] ,其中 N 是以实现定义的 16 位编码(通常为 UTF-16 )的编码单元数计的字符串大小,包括空终止符。如同通过在实现定义的本地环境中执行 mbrtoc16 初始化数组的每个 char16_t 元素。
4) 32 位宽字符串字面量:字面量的类型为 char32_t[N] ,其中 N 是以实现定义的 32 位编码(通常为 UTF-32 )的编码单元数计的字符串大小,包括空终止符。如同通过在实现定义的本地环境中执行 mbrtoc32 初始化数组的每个 char32_t 元素。
5) 宽字符串字面量:字面量的类型为 wchar_t[N] ,其中 N 是以执行宽编码的编码单元数计的字符串大小,包括空终止符。如同通过在实现定义的本地环境中执行 mbstowcs 初始化数组的每个 wchar_t 元素。

解释

首先,在翻译阶段 6 (宏展开后),连接相邻的字符串字面量(即仅为空白符所分隔的字符串字面量)。

仅可连接二个窄或二个宽字符串字面量。

(C99 前)

若一个字面量无前缀,则结果字符串字面量拥有有前缀字面量所指定的宽度/编码。若二个字符串字面量拥有不同的编码前缀,则连接是实现定义的。

L"Δx = %" PRId16 // 在阶段 4 , PRId16 展开成 "d"
                 // 在阶段 6 , L"Δx = %" 和 "d" 组成 L"Δx = %d"
(C99 起)

其次,在翻译阶段 7 ,添加空终止字符到每个字符串字面量,然后每个字符串字面量初始化一个拥有静态存储期,长度刚好能容纳字符串字面量的内容加一个空终止符的无名数组。

char* p = "\x12" "3"; // 创建 static char[3] 数组,保有 {'\x12', '3', '\0'} 
                      // 设置 p 指向数组的首元素

字符串字面量不可修改(而且实际上可以放在例如 .rodata 的只读内存中)。若程序试图修改字符串字面量组成的静态数组,则行为未定义。

char* p = "Hello";
p[1] = 'M'; // 未定义行为
char a[] = "Hello";
a[1] = 'M'; // OK : a 不是字符串字面量

既不要求亦不禁止等同的字符串字面量指代内存中的同一位置。而且,重叠的字符串字面量或作为其他字符串字面量子串的字符串字面量可以结合。

"def" == 3+"abcdef"; // 可为 1 或 0 ,实现定义

注意

字符串字面量不必是一条字符串;若字符串字面量拥有内嵌的空字符,则它表示含有多于一条字符串的数组:

char* p = "abc\0def"; // strlen(p) == 3 ,但数组大小为 8

若在字符串字面量中合法十六进制数位后随十六进制转义,则这会作为非法的转义序列导致编译失败,但能以字符串连接为变通方式:

//char* p = "\xfff"; // 错误:十六进制转义在范围外
char* p = "\xff""f"; // OK :字面量为 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 选择它们。

示例

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <uchar.h>
#include <locale.h>
int main(void)
{
    char s1[] = "a猫🍌"; // 或 "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 ", (unsigned)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  }

引用

  • C11 standard (ISO/IEC 9899:2011):
  • 6.4.5 String literals (p: 70-72)
  • C99 standard (ISO/IEC 9899:1999):
  • 6.4.5 String literals (p: 62-63)
  • C89/C90 standard (ISO/IEC 9899:1990):
  • 3.1.4 String literals

参阅