C++ 用C字符串初始化std::string的奇怪方法
在阅读nVidia CUDA源代码时,我偶然发现了以下两行代码:C++ 用C字符串初始化std::string的奇怪方法,c++,c,string,cuda,C++,C,String,Cuda,在阅读nVidia CUDA源代码时,我偶然发现了以下两行代码: std::string stdDevString; stdDevString = std::string(device_string); 请注意,设备_字符串是一个字符[1024]。问题是:为什么要构造一个空的std::string,然后用一个C字符串作为参数再次构造它?为什么他们不调用std::string stdDevString=std::string(设备字符串)仅在一行中 是否存在此代码试图规避/使用
std::string stdDevString;
stdDevString = std::string(device_string);
请注意,设备_字符串是一个字符[1024]。问题是:为什么要构造一个空的std::string,然后用一个C字符串作为参数再次构造它?为什么他们不调用std::string stdDevString=std::string(设备字符串)代码>仅在一行中
是否存在此代码试图规避/使用的隐藏字符串初始化行为?是为了确保stdDevString中的C字符串在任何情况下都保持null终止?因为据我所知,将std::string初始化为不以null结尾的C字符串仍然会出现问题
为什么他们不调用std::string stdDevString=std::string(设备字符串)代码>仅在一行中
他们这样做没有充分的理由。给定std::string::string(const char*)
构造函数,您只需使用以下任意一种:
std::string stdDevString = device_string;
std::string stdDevString(device_string);
std::string stdDevString{device_string}; // C++11 { } syntax
两步默认构造然后分配只是(糟糕的)程序员风格或疏忽。如果没有优化,它确实会进行一些不必要的构建,但这仍然相当便宜。它很可能通过优化而消除。没什么大不了的-我怀疑我是否会费心在代码评审中提到它,除非它是在一个性能非常敏感的领域,但绝对最好推迟声明变量,直到有一个有用的初始值可用来构造它们,将所有变量都放在一个地方:它不仅不容易出错,而且可以交叉引用,但它最小化了变量的范围,简化了使用变量的推理
是为了确保stdDevString
中的C字符串无论发生什么情况都以null结尾
不,这没什么区别。由于C++11,无论使用哪种构造函数,stdDevString
中的内部缓冲区都将保持NUL终止,而对于C++03,不一定终止-请参见下面C++03详细信息的专用标题-但无论如何进行构造/分配,都无法保证
因为据我所知,将std::string
初始化为非空终止的C字符串仍然会出现问题
您是对的-您列出的任何构造选项都只会将ASCIIZ文本复制到std::string
-考虑到第一个NUL('\0'
)终止符。如果字符数组不是NUL终止的,则会出现问题
(对于std::string
中的缓冲区是否保持NUL终止,这是一个单独的问题——上面已经讨论过)
请注意,有一个单独的字符串(const char*,size_type)
构造函数,它可以创建带有嵌入NUL的字符串,并且不会尝试读取更多的内容(构造函数(4))
C++03 std::字符串不保证在内部以NUL终止
无论以何种方式构造和初始化std::string
,在C++11之前,标准不要求它在字符串的缓冲区内以NUL结尾std::string
最好被想象为包含一组潜在的不可打印字符(松散地说,是ftp/文件I/O意义上的二进制字符),从地址data()
开始,扩展为size()
字符。所以,如果你有:
std::string x("help");
x[4]; // undefined behaviour: only [0]..[3] are safe
x.at(4); // will throw rather than return '\0'
x.data()[4]; // undefined behaviour, equivalent to x[4] above
x.c_str()[4]; // safely returns '\0', (perhaps because a NUL was always
// at x[4], one was just added, or a new NUL-terminated
// buffer was just prepared - in which case data() may
// or may not start returning it too)
请注意,std::string API需要c_str()
返回指向NUL终止值的指针。为此,它可以:
- 始终主动在字符串缓冲区的末尾保留一个额外的
(在这种情况下,NUL
恰好在该实现上是安全的,但如果实现发生更改或代码被移植到另一个标准库实现等,则代码可能会中断。)data[5]
- 反应性地等待,直到调用
,然后:c_str()
- 如果它在当前地址有足够的容量(即
),则附加一个data()
,并返回与NUL
返回的指针值相同的指针值data()
- 否则,分配一个新的、更大的缓冲区,复制数据,
终止它,并返回指向它的指针(通常但也可以选择此缓冲区将替换将被删除的旧缓冲区,这样,随后立即调用NUL
将返回由data()
返回的相同指针)c_str()
- 如果它在当前地址有足够的容量(即
std::string stdDevString = std::string(device_string);
或者更简单一点:
std::string stdDevString = device_string;
一旦创建了std::string,它就包含了C字符串中数据的私有副本。在我看来就像一件艺术品。也许中间还有其他代码,后来被删除了,有人懒得把剩下的两行合并成一行。我认为把这看作是糟糕的编码是无知的。如果我们假设这个字符串是在文件范围内分配的,或者是作为一个静态变量分配的,那么它可能是一个很好的编码
在对具有非易失性内存的嵌入式系统进行C++编程时,有很多理由希望避免静态初始化:主要原因是程序开始时增加了很多开销代码,所有这些变量都被初始化。如果它们是类的实例,则将调用构造函数
这将导致程序执行开始时出现延迟峰值。您不希望出现这种工作负载高峰,因为在启动程序时有更重要的任务要做,比如设置各种硬件 为了避免这种情况,您通常会在编译器中启用一个选项来删除此类静态初始化,然后以这样一种方式编写代码:不初始化静态/全局变量,而是在运行时设置它们在这样的系统中,OP发布的代码是正确的方法。Dunno,对我来说就像对你一样多余。事实上,它可能初始化了两次。但是,话说回来,也许