主要内容
c++17标准发布,string_view是标准新增的内容。这篇文章主要分析string_view的适用范围、注意事项,并分析string_view带来的性能提升,最后从gcc 8.2的libstdc++库源码级别分析性能提升的原因。
背景知识:静态字符串的处理
所谓静态字符串,就是编译时已经固定的字符串,他们存储在二进制文件的静态存储区,而且程序只能读取,不能改动。
一个例子:
//指针指向静态字符串 const char* str_ptr = "this is a static string"; //字符串数组 char str_array[] = "this is a static string"; //std::string std::string str = "this is a static string"; //std::string_view std::string_view sv = "this is a static string";
反汇编:
g++ -o0 -o static_str static_str.cc -std=c++17 -g && objdump -s -t -d static_str > static_str.s
汇编代码如下:
int main() { 4013b8: 55 push %rbp 4013b9: 48 89 e5 mov %rsp,%rbp 4013bc: 53 push %rbx 4013bd: 48 83 ec 68 sub $0x68,%rsp //指针指向静态字符串 const char* str_ptr = "this is a static string!"; ##直接设置字符串指针 4013c1: 48 c7 45 e8 30 1e 40 movq $0x401e30,-0x18(%rbp) 4013c8: 00 //字符串数组 char str_array[] = "this is a static string!"; ##这里使用一个很取巧的办法,不使用循环,而是使用多个mov语句把字符串设置到堆栈 4013c9: 48 b8 74 68 69 73 20 mov $0x2073692073696874,%rax 4013d0: 69 73 20 4013d3: 48 ba 61 20 73 74 61 mov $0x6369746174732061,%rdx 4013da: 74 69 63 4013dd: 48 89 45 c0 mov %rax,-0x40(%rbp) 4013e1: 48 89 55 c8 mov %rdx,-0x38(%rbp) 4013e5: 48 b8 20 73 74 72 69 mov $0x21676e6972747320,%rax 4013ec: 6e 67 21 4013ef: 48 89 45 d0 mov %rax,-0x30(%rbp) 4013f3: c6 45 d8 00 movb $0x0,-0x28(%rbp) //std::string std::string str = "this is a static string!"; #esi保存了字符串开始地址$0x401e30,调用std::string的构造函数 4013f7: 48 8d 45 e7 lea -0x19(%rbp),%rax 4013fb: 48 89 c7 mov %rax,%rdi 4013fe: e8 15 fe ff ff callq 401218 <_znsaicec1ev@plt> 401403: 48 8d 55 e7 lea -0x19(%rbp),%rdx 401407: 48 8d 45 a0 lea -0x60(%rbp),%rax 40140b: be 30 1e 40 00 mov $0x401e30,%esi 401410: 48 89 c7 mov %rax,%rdi 401413: e8 fe 01 00 00 callq 401616 <_znst7__cxx1112basic_stringicst11char_traitsicesaiceec1is3_eepkcrks3_> 401418: 48 8d 45 e7 lea -0x19(%rbp),%rax 40141c: 48 89 c7 mov %rax,%rdi 40141f: e8 c4 fd ff ff callq 4011e8 <_znsaiced1ev@plt> //std::string_view std::string_view sv = "this is a static string!"; #直接设置字符串的长度0x18,也就是24bytes,还有字符串的起始指针$0x401e30,没有堆内存分配 401424: 48 c7 45 90 18 00 00 movq $0x18,-0x70(%rbp) 40142b: 00 40142c: 48 c7 45 98 30 1e 40 movq $0x401e30,-0x68(%rbp) 401433: 00 return 0; 401434: bb 00 00 00 00 mov $0x0,%ebx //字符串数组 ## 对象析构:字符串数组分配在栈上,无需析构 char str_array[] = "this is a static string!"; //std::string ## 对象析构:调用析构函数 std::string str = "this is a static string!"; 401439: 48 8d 45 a0 lea -0x60(%rbp),%rax 40143d: 48 89 c7 mov %rax,%rdi 401440: e8 a9 01 00 00 callq 4015ee <_znst7__cxx1112basic_stringicst11char_traitsicesaiceed1ev> 401445: 89 d8 mov %ebx,%eax 401447: eb 1a jmp 401463 <main+0xab> 401449: 48 89 c3 mov %rax,%rbx 40144c: 48 8d 45 e7 lea -0x19(%rbp),%rax 401450: 48 89 c7 mov %rax,%rdi 401453: e8 90 fd ff ff callq 4011e8 <_znsaiced1ev@plt> 401458: 48 89 d8 mov %rbx,%rax 40145b: 48 89 c7 mov %rax,%rdi 40145e: e8 e5 fd ff ff callq 401248 <_unwind_resume@plt> //std::string_view ## 对象析构:std::string_view分配在栈上,无需析构 std::string_view sv = "this is a static string!"; return 0; }
- 静态字符串:会把指针指向静态存储区,字符串只读。如果尝试修改,会导致段错误(segment fault)。
- 字符串数组:在栈上分配一块空间,长度等于字符串的长度+1(因为还需要包括末尾的’