使用C字符串初始化std :: string的奇怪方法

时间:2011-02-26 18:29:07

标签: c++ c string cuda

在我阅读nVidia CUDA源代码时,我偶然发现了这两行:

    std::string stdDevString;

    stdDevString = std::string(device_string);

请注意,device_string是char [1024]。问题是:为什么要构造一个空的std :: string,然后用C字符串作为参数再次构造它?他们为什么不在一行中拨打std::string stdDevString = std::string(device_string);

是否存在此代码试图逃避/使用的隐藏字符串初始化行为?是确保stdDevString里面的C字符串无论什么都保持null终止?因为据我所知,将std :: string初始化为非空终止的C字符串仍会出现问题。

4 个答案:

答案 0 :(得分:4)

  

他们为什么不在一行中拨打std::string stdDevString = std::string(device_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字符串无论什么都保持空终止?

不 - 这没有任何区别。从C ++ 11开始,无论使用哪个构造函数,stdDevString中的内部缓冲区都将保持NUL终止,而对于C ++ 03并不一定终止 - 请参阅下面的C ++ 03详细专用标题 - 但无论结构/作业如何完成,都无法保证。

  

因为据我所知,将std::string初始化为非空终止的C字符串仍会出现问题。

你说得对 - 你列出的任何构造选项都只会将ASCIIZ文本复制到std::string - 考虑到第一个NUL('\0')终结符。如果char数组不是NUL终止的,那么就会出现问题。

(这是一个单独的问题,std::string内的缓冲区是否保持NUL终止 - 上面讨论过。

请注意,有一个单独的string(const char*, size_type)构造函数, 可以 创建具有嵌入式NUL的字符串,并且不会尝试进一步阅读(构造函数(4) )here

C ++ 03 std :: strings无法保证在内部终止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()将返回的相同指针值< / LI>
    • 否则,分配一个新的,更大的缓冲区,复制数据,NUL终止它,并返回指向它的指针(通常是但是可选地这个缓冲区将替换旧的缓冲区,将被删除,以便之后立即调用data()将返回c_str()返回的相同指针

答案 1 :(得分:1)

我会说它相当于写作:

std::string stdDevString = std::string(device_string);

或者,甚至更简单:

std::string stdDevString = device_string;

创建std :: string后,它包含C字符串中数据的私有副本。

答案 2 :(得分:1)

我认为将此视为不良编码是无知的。如果我们假设这个字符串是在文件范围或静态变量中分配的,那么它可能是良好的编码。

当为具有非易失性存储器的嵌入式系统编程C ++时,有许多原因可以避免静态初始化:主要原因是它在程序开头添加了大量开销代码,其中所有这些变量都是很多被初始化。如果它们是类的实例,则将调用构造函数。

这将导致程序执行开始时的延迟峰值。您希望此工作负载达到峰值,因为在启动程序时还有许多重要的任务要做,例如设置各种硬件。

为了避免这种情况,通常在编译器中启用一个选项来删除这样的静态初始化,然后以不初始化静态/全局变量的方式编写代码,而是在运行时设置它们。

在这样的系统上,OP发布的代码是正确的方法。

答案 3 :(得分:0)

对我来说看起来像是一件艺术品。也许之间还有一些其他的代码,然后它被删除了,有人懒得将这两行剩余的行加入到一个单独的代码中。