字符串字面量
cout <<"hello"<< endl;
在上面代码中“hello"是一个字符串常量,因为这个字符串以值的方式给出,而不是一个变量。它存储在内存中的只读部分。通过这种方式,编译器可重用相同字符串字面量的引用。从而优化内存的使用。即使一个程序中使用几百次”hello“字符串字面量,编译器也旨在内存中创建一个hello实例,这种技术叫做字面量池。
字符串字面量可以赋值给变量,但是因为字符串字面量位于内存的只读部分,且使用了字面量池,所以这样做会产生风险。c++标准正式指出:字符串字面量的类型为”n个const char*的数组“,然而为了向后兼容较老的不支持const的代码,大部分编译器不会强制程序将字符串字面量赋值给const char *类型的变量。这些编译器允许将字符串字面量赋值给不带有const的char *,而且整个程序可以正常运行,除非试图修改字符串,一般情况下,修改字符串字面量的行为是未定义的。可能会导致程序崩溃,也可能继续执行,会产生一些莫名其妙的副作用;具体是否生效主要是看编译器。例如下面的未定义行为:
char *p {"hello"};
p[1] = 'a'; // undefined behavior
一种更安全的编码方式是在引用字符串常量的时候,使用指向const字符的指针。下面的代码包含同样的bug,到那时由于这段代码将字符串字面量赋值给const char *,因此编译器会捕捉到任何写入只读内存的企图。
const char* p {"hello"};
p[1] = 'a'; // ERROR!Attempts to write read-only memory
可以将字符串字面量作为字符数组的(char[])的初始值,这样,编译器会创建一个足以存下这个字符串的数组,然后将字符串复制到这个数组中,因此,编译器不会将字面量存放在只读区域,也不会进行字面量池操作。
char arr[] {"hello"};
arr[1] = 'a'; // can modified
原始字符串字面值
原始字符串字面量(raw string literal)是可横跨多行代码的字符串字面量,需要转义嵌入的双引号,像\t,\n这种转义序列不按照转义序列的方式处理,而是按照普通文本方式处理。例:
const char* str {"hello "world"!"}; // error
// 对于普通字符串必须转义双引号,如下
const char* str {"hello \"world\"!"};
对于原始字符串字面量,就不需要转义双引号了,原始字符串字面量以R“(开头,以”)结尾。
const char* str {R"(hello "world"!)"};
下面两个写法,输出的格式是相同的
const char* str {"line1 \nline2"};
const char* str {R"(line1
line2)"};
因为原始字符串字面量是以)“结尾,所以使用该语法时,不能在字符串中嵌入)”。例如
const char* str {R"(hello)" world)"}; // error
如果需要嵌入)",则需要使用扩展的原始字符串字面量语法, 如下:
R”d-char-sequence(r-char-sequence)d-char-sequence"
r-char-sequence是实际的原始字符串,d-char-sequence是可选的分隔符序列,原始字符串首尾的分隔符序列应该一致。分隔符最多能有16个字符,应该选择为出现在原始字符串字面量中的序列作为分隔符序列。上述事例可以改为:
const char* str {R"-(hello)" world)-"};
在操作数据库查询字符串、正则表达式和文件路径时,原始字符串字面量可以令程序的编写更加方便。