string_view: No such file or directory
std::string_view
is not available in GCC until version 7.
root@ubuntu:~/c++# gcc --version gcc (Ubuntu/Linaro 5.5.0-12ubuntu1) 5.5.0 20171010 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. root@ubuntu:~/c++#
configure: error: Building GCC requires GMP 4.2+, MPFR 2.4.0+ and MPC 0.8.0+.
root@cloud:~/gcc-7.5.0# ./contrib/download_prerequisites 2021-07-02 15:02:50 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.1.0.tar.bz2 [2383840] -> "./gmp-6.1.0.tar.bz2" [1] 2021-07-02 15:02:59 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/mpfr-3.1.4.tar.bz2 [1279284] -> "./mpfr-3.1.4.tar.bz2" [1] 2021-07-02 15:03:05 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.0.3.tar.gz [669925] -> "./mpc-1.0.3.tar.gz" [1] 2021-07-02 15:03:15 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-0.16.1.tar.bz2 [1626446] -> "./isl-0.16.1.tar.bz2" [1] gmp-6.1.0.tar.bz2: OK mpfr-3.1.4.tar.bz2: OK mpc-1.0.3.tar.gz: OK isl-0.16.1.tar.bz2: OK All prerequisites downloaded successfully. root@cloud:~/gcc-7.5.0#
root@cloud:~/gcc-7.5.0# mkdir gcc-build-7.5 root@cloud:~/gcc-7.5.0# cd gcc-build-7.5/ root@cloud:~/gcc-7.5.0/gcc-build-7.5# ../configure --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --enable-gnu-indirect-function --with-tune=generic --disable-multilib
root@cloud:~/gcc-7.5.0# ./contrib/download_prerequisites 2021-07-02 15:02:50 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.1.0.tar.bz2 [2383840] -> "./gmp-6.1.0.tar.bz2" [1] 2021-07-02 15:02:59 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/mpfr-3.1.4.tar.bz2 [1279284] -> "./mpfr-3.1.4.tar.bz2" [1] 2021-07-02 15:03:05 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.0.3.tar.gz [669925] -> "./mpc-1.0.3.tar.gz" [1] 2021-07-02 15:03:15 URL: ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-0.16.1.tar.bz2 [1626446] -> "./isl-0.16.1.tar.bz2" [1] gmp-6.1.0.tar.bz2: OK mpfr-3.1.4.tar.bz2: OK mpc-1.0.3.tar.gz: OK isl-0.16.1.tar.bz2: OK All prerequisites downloaded successfully. root@cloud:~/gcc-7.5.0# cd - /root/gcc-7.5.0/gcc-build-7.5 root@cloud:~/gcc-7.5.0/gcc-build-7.5# ../configure --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --enable-gnu-indirect-function --with-tune=generic --disable-multilib checking build system type... aarch64-unknown-linux-gnu checking host system type... aarch64-unknown-linux-gnu checking target system type... aarch64-unknown-linux-gnu checking for a BSD-compatible install... /usr/bin/install -c checking whether ln works... yes checking whether ln -s works... yes checking for a sed that does not truncate output... /bin/sed checking for gawk... gawk checking for libatomic support... yes checking for libcilkrts support... no checking for libitm support... yes checking for libsanitizer support... yes checking for libvtv support... yes checking for libmpx support... no checking for libhsail-rt support... no
checking if mkdir takes one argument... no This target does not support --with-tune. Valid --with options are: abi cpu arch Makefile:4282: recipe for target 'configure-stage1-gcc' failed make[2]: *** [configure-stage1-gcc] Error 1 make[2]: Leaving directory '/root/gcc-7.5.0/gcc-build-7.5' Makefile:25895: recipe for target 'stage1-bubble' failed make[1]: *** [stage1-bubble] Error 2 make[1]: Leaving directory '/root/gcc-7.5.0/gcc-build-7.5' Makefile:939: recipe for target 'all' failed make: *** [all] Error 2 root@cloud:~/gcc-7.5.0/gcc-build-7.5#
configure的最后一个参数是关闭32位库,只编译64位库, 。如果想要同时编译32位和64位,可以使用-enable-multilib
../configure --enable-checking=release --enable-languages=c,c++ --disable-multilib
编译通过
make install
root@cloud:~/gcc-7.5.0/gcc-build-7.5# ls /usr/local/bin | grep gcc aarch64-unknown-linux-gnu-gcc aarch64-unknown-linux-gnu-gcc-7.5.0 aarch64-unknown-linux-gnu-gcc-ar aarch64-unknown-linux-gnu-gcc-nm aarch64-unknown-linux-gnu-gcc-ranlib gcc gcc-ar gcc-nm gcc-ranlib
-rwxr-xr-x 3 root root 5771384 Jul 2 17:43 /usr/local/bin/aarch64-unknown-linux-gnu-gcc-7.5.0 root@cloud:~/gcc-7.5.0/gcc-build-7.5# ls -al /usr/local/bin/aarch64-unknown-linux-gnu-gcc -rwxr-xr-x 3 root root 5771384 Jul 2 17:43 /usr/local/bin/aarch64-unknown-linux-gnu-gcc root@cloud:~/gcc-7.5.0/gcc-build-7.5# ls -al /usr/local/bin/gcc -rwxr-xr-x 3 root root 5771384 Jul 2 17:43 /usr/local/bin/gcc root@cloud:~/gcc-7.5.0/gcc-build-7.5# /usr/local/bin/gcc -v Using built-in specs. COLLECT_GCC=/usr/local/bin/gcc COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/aarch64-unknown-linux-gnu/7.5.0/lto-wrapper Target: aarch64-unknown-linux-gnu Configured with: ../configure --enable-checking=release --enable-languages=c,c++ --disable-multilib Thread model: posix gcc version 7.5.0 (GCC) root@cloud:~/gcc-7.5.0/gcc-build-7.5#
root@cloud:~/gcc-7.5.0/gcc-build-7.5# find /usr -name "libstdc++.so*" /usr/local/lib64/libstdc++.so /usr/local/lib64/libstdc++.so.6.0.24-gdb.py /usr/local/lib64/libstdc++.so.6.0.24 /usr/local/lib64/libstdc++.so.6 /usr/lib/gcc/aarch64-linux-gnu/5/libstdc++.so /usr/lib/gcc/aarch64-linux-gnu/7/libstdc++.so /usr/lib/aarch64-linux-gnu/libstdc++.so.6.0.25 /usr/lib/aarch64-linux-gnu/libstdc++.so.6 /usr/share/gdb/auto-load/usr/lib/aarch64-linux-gnu/libstdc++.so.6.0.25-gdb.py root@cloud:~/gcc-7.5.0/gcc-build-7.5#
g++ -O0 -o static_str str_vier.cpp -std=c++17 -g && objdump -S -t -D static_str > static_str.s
#include<string> #include<string_view> using namespace std; int main() { 40150c: a9b87bfd stp x29, x30, [sp, #-128]! 401510: 910003fd mov x29, sp 401514: f9000bf3 str x19, [sp, #16] //指针指向静态字符串 const char* str_ptr = "this is a static string"; 401518: 90000000 adrp x0, 401000 <_init-0x2c0> 40151c: 913ee000 add x0, x0, #0xfb8 401520: f9003fa0 str x0, [x29, #120] //字符串数组 char str_array[] = "this is a static string"; 401524: 90000000 adrp x0, 401000 <_init-0x2c0> 401528: 913ee000 add x0, x0, #0xfb8 40152c: 910163a2 add x2, x29, #0x58 401530: aa0003e3 mov x3, x0 401534: a9400460 ldp x0, x1, [x3] 401538: a9000440 stp x0, x1, [x2] 40153c: f9400860 ldr x0, [x3, #16] 401540: f9000840 str x0, [x2, #16] //std::string std::string str = "this is a static string"; 401544: 9101c3a0 add x0, x29, #0x70 401548: 97ffffa6 bl 4013e0 <_ZNSaIcEC1Ev@plt> 40154c: 9101c3a2 add x2, x29, #0x70 401550: 90000000 adrp x0, 401000 <_init-0x2c0> 401554: 913ee001 add x1, x0, #0xfb8 401558: 9100e3a0 add x0, x29, #0x38 40155c: 94000075 bl 401730 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcRKS3_> 401560: 9101c3a0 add x0, x29, #0x70 401564: 97ffff8b bl 401390 <_ZNSaIcED1Ev@plt> //std::string_view std::string_view sv = "this is a static string"; 401568: d28002e0 mov x0, #0x17 // #23 40156c: f90017a0 str x0, [x29, #40] 401570: 90000000 adrp x0, 401000 <_init-0x2c0> 401574: 913ee000 add x0, x0, #0xfb8 401578: f9001ba0 str x0, [x29, #48] std::string str = "this is a static string"; 40157c: 9100e3a0 add x0, x29, #0x38 401580: 94000062 bl 401708 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev> }
#include<string> #include<string_view> using namespace std; int main() { //指针指向静态字符串 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"; }
当字符串数据的所有权已经确定(譬如由某个string对象持有),并且你只想访问(而不修改)他们时,使用 std::string_view 可以避免字符串数据的复制,从而提高程序效率,这(指程序效率)也是这篇文章的主要内容.
这次要介绍的 string_view 是 C++17 的一个主要特性.
我假设你已经了解了一些 std::string_view 的知识,如果没有,可以看看我之前的这篇文章.C++ 中的 string 类型在堆上存放自己的字符串数据,所以当你处理 string 类型的时候,很容易就会产生(堆)内存分配.
Small string optimisation
我们先看下以下的示例代码:
1 #include <iostream> 2 #include <string> 3 4 void* operator new(std::size_t count) 5 { 6 std::cout << " " << count << " bytes" << std::endl; 7 return malloc(count); 8 } 9 10 void getString(const std::string& str) {} 11 12 int main() 13 { 14 std::cout << std::endl; 15 16 std::cout << "std::string" << std::endl; 17 18 std::string small = "0123456789"; 19 std::string substr = small.substr(5); 20 std::cout << " " << substr << std::endl; 21 22 std::cout << std::endl; 23 24 std::cout << "getString" << std::endl; 25 26 getString(small); 27 getString("0123456789"); 28 const char message[] = "0123456789"; 29 getString(message); 30 31 std::cout << std::endl; 32 33 return 0; 34 }
代码第4到第8行,我重载了全局的 new 操作符,这样我就能跟踪(堆)内存的分配了,而后,代码分别在第18行,第19行,第27行,第29行创建了string对象,所以这几处代码都会产生(堆)内存分配.相关的程序输出如下:
root@cloud:~/c++# ./srV1 std::string 56789 getString root@cloud:~/c++#
咦, 程序竟然没有产生内存分配?这是怎么回事?其实 string 类型只有在字符串超过指定大小(具体实现相关)时才会申请(堆)内存,对于 MSVC 来说,指定大小为 15, 对于 GCC 和 Clang,这个值则为 23.
这也就意味着,较短的字符串数据是直接存储于 string 的对象内存中的,不需要分配(堆)内存.
从现在开始,示例代码中的字符串将拥有至少30个字符,这样我们就不需要关注短字符串优化了.好了,带着这个前提(字符串长度>=30个字符),让我们重新开始讲解.
#include <iostream> #include <string> void* operator new(std::size_t count) { std::cout << " " << count << " bytes" << std::endl; return malloc(count); } void getString(const std::string& str) {} int main() { std::cout << std::endl; std::cout << "std::string" << std::endl; std::string small = "0123456789#0123456789#0123456789#0123456789#0123456789"; std::string substr = small.substr(5); std::cout << " " << substr << std::endl; std::cout << std::endl; std::cout << "getString" << std::endl; getString(small); getString("0123456789"); const char message[] = "0123456789"; getString(message); std::cout << std::endl; return 0; }
root@cloud:~/c++# ./srV1 std::string 55 bytes 50 bytes 56789#0123456789#0123456789#0123456789#0123456789 getString
#include <iostream> #include <string> void* operator new(std::size_t count) { std::cout << " " << count << " bytes" << std::endl; return malloc(count); } void getString(const std::string& str) {} int main() { std::cout << std::endl; std::cout << "std::string" << std::endl; std::string small = "0123456789#0123456789#0123456789#0123456789#0123456789"; getString(small); getString("01234567890123456789#0123456789#0123456789#0123456789#0123456789"); const char message[] = "01234567890123456789#0123456789#0123456789#0123456789#0123456789$$$$$$$$$$$$$"; getString(message); std::cout << std::endl; return 0; }
root@cloud:~/c++# g++ -std=c++17 -g strV1.cpp -o srV1 root@cloud:~/c++# ./srV1 std::string 55 bytes 65 bytes 78 bytes root@cloud:~/c++#
#include <iostream> #include <string> #include <string_view> void* operator new(std::size_t count) { std::cout << " " << count << " bytes" << std::endl; return malloc(count); } void getString(const std::string& str) {} int main() { std::cout << std::endl; std::cout << "std::string" << std::endl; std::string large= "0123456789#0123456789#0123456789#0123456789#0123456789"; std::string_view largeStringView{ large.c_str(), large.size() }; std::cout << std::endl; return 0; }
root@cloud:~/c++# g++ -std=c++17 -g strV1.cpp -o srV1 root@cloud:~/c++# ./srV1 std::string 55 bytes
No memory allocation required
现在, std::string_view 无需复制字符串数据的优点就更加明显了(std::string不进行短字符串优化的情况下),下面的代码就是例证.
1 #include <cassert> 2 #include <iostream> 3 #include <string> 4 #include <string_view> 5 6 void* operator new(std::size_t count) 7 { 8 std::cout << " " << count << " bytes" << std::endl; 9 return malloc(count); 10 } 11 12 void getString(const std::string& str) {} 13 14 void getStringView(std::string_view strView) {} 15 16 int main() 17 { 18 std::cout << std::endl; 19 20 std::cout << "std::string" << std::endl; 21 22 std::string large = "0123456789-123456789-123456789-123456789"; 23 std::string substr = large.substr(10); 24 25 std::cout << std::endl; 26 27 std::cout << "std::string_view" << std::endl; 28 29 std::string_view largeStringView{ large.c_str(), large.size() }; 30 largeStringView.remove_prefix(10); 31 32 assert(substr == largeStringView); 33 34 std::cout << std::endl; 35 36 std::cout << "getString" << std::endl; 37 38 getString(large); 39 getString("0123456789-123456789-123456789-123456789"); 40 const char message[] = "0123456789-123456789-123456789-123456789"; 41 getString(message); 42 43 std::cout << std::endl; 44 45 std::cout << "getStringView" << std::endl; 46 47 getStringView(large); 48 getStringView("0123456789-123456789-123456789-123456789"); 49 getStringView(message); 50 51 std::cout << std::endl; 52 53 return 0; 54 }
root@cloud:~/c++# mv controller.yaml strV2.cpp root@cloud:~/c++# g++ -std=c++17 -g strV2.cpp -o srV2 root@cloud:~/c++# ./srV2 std::string 41 bytes 31 bytes std::string_view getString 41 bytes 41 bytes getStringView
#include <iostream> #include <string> #include <string_view> #include <cassert> void* operator new(std::size_t count) { std::cout << " " << count << " bytes" << std::endl; return malloc(count); } void getString(const std::string& str) {} int main() { std::cout << std::endl; std::cout << "std::string" << std::endl; std::string large= "0123456789#0123456789#0123456789#0123456789#0123456789"; std::string substr = large.substr(10); std::cout << std::endl; std::cout << "std::string_view" << std::endl; std::string_view largeStringView{ large.c_str(), large.size() }; largeStringView.remove_prefix(10); assert(substr == largeStringView); std::cout << substr << std::endl; std::cout << largeStringView << std::endl; std::cout << std::endl; std::cout << "getString" << std::endl; retu
root@cloud:~/c++# ./srV1 std::string 55 bytes 45 bytes std::string_view #0123456789#0123456789#0123456789#0123456789 #0123456789#0123456789#0123456789#0123456789
#include <iostream> #include <string> #include <string_view> #include <cassert> void* operator new(std::size_t count) { std::cout << " " << count << " bytes" << std::endl; return malloc(count); } void getString(const std::string& str) {} void getStringView(std::string_view strView) {} int main() { std::cout << std::endl; std::cout << "std::string" << std::endl; std::string large= "0123456789#0123456789#0123456789#0123456789#0123456789"; std::cout << std::endl; std::cout << "std::string_view" << std::endl; std::string_view largeStringView{ large.c_str(), large.size() }; std::cout << std::endl; std::cout << "getString" << std::endl; //getString(large); //getString("0123456789-123456789-123456789-123456789"); const char message[] = "0123456789-123456789-123456789-123456789"; //getString(message); std::cout << std::endl; std::cout << "getStringView" << std::endl; getStringView(large); getStringView("0123456789-123456789-123456789-123456789"); getStringView(message); std::cout << std::endl; return 0; }
root@cloud:~/c++# g++ -std=c++17 -g strV1.cpp -o srV1 root@cloud:~/c++# ./srV1 std::string 55 bytes std::string_view getString getStringView