转换c_str()仅适用于短字符串

时间:2016-03-14 17:02:18

标签: c++ string pointers c++11 casting

我在C ++中使用C库并编写了一个包装器。有一次,我需要将std::string转换为c风格的字符串。有一个带有函数的类,它返回一个字符串。如果字符串很短,则转换返回的字符串,否则不起作用。以下是一个简单且简化的示例,说明了该问题:

#include <iostream>
#include <string>

class StringBox {
public:
  std::string getString() const { return text_; }

  StringBox(std::string text) : text_(text){};

private:
  std::string text_;
};

int main(int argc, char **argv) {
  const unsigned char *castString = NULL;
  std::string someString = "I am a loooooooooooooooooong string";  // Won't work
  // std::string someString = "hello";  // This one works

  StringBox box(someString);

  castString = (const unsigned char *)box.getString().c_str();
  std::cout << "castString: " << castString << std::endl;

  return 0;
}

执行上述文件将其打印到控制台:

  

castString:

如果我在someString上交换评论,则会正确打印

  

castString:hello

这怎么可能?

5 个答案:

答案 0 :(得分:16)

您正在对c_str成员函数重新调整的临时字符串对象调用getString()c_str()返回的指针仅在原始字符串对象存在时才有效,因此在您指定castString的行的末尾,它最终成为悬空指针。正式来说,这会导致不确定的行为。

那为什么这对短串有用呢?我怀疑你是否看到了短字符串优化的影响,这是一种优化,对于字符串小于一定长度的字符串数据存储在字符串对象本身的字节内而不是堆中。返回的临时字符串可能存储在堆栈中,因此在清理时没有发生解除分配,指向过期字符串对象的指针仍然保留旧的字符串字节。这似乎与您所看到的一致,但它仍然并不意味着您所做的事情是个好主意。 : - )

答案 1 :(得分:6)

box.getString()匿名临时c_str()仅对变量的长度有效。

因此,在您到达c_str()时,std::cout 无效。读取指针内容的行为是 undefined

(有趣的是,由于std::string以不同的方式存储短字符串,短字符串的行为可能不同。)

答案 2 :(得分:5)

按价值返回时

box.getString()是暂时的,所以

box.getString().c_str()仅在表达式期间有效,然后它是一个悬空指针。

你可以用

解决这个问题
const std::string& getString() const { return text_; }

答案 3 :(得分:5)

box.getString()产生一个临时的。在其上调用c_str()会为您提供指向临时的指针。暂时不再存在后,指针无效,悬空指针

使用悬空指针是Undefined Behavior。

答案 4 :(得分:4)

首先,您的代码的UB与字符串的长度无关:在

的末尾
castString = (const unsigned char *)box.getString().c_str();

getString返回的字符串被销毁,castString是指向被破坏的字符串对象的内部缓冲区的悬空指针。

您的代码&#34;工作的原因&#34;对于小字符串可能是小字符串优化:短字符串(通常)保存在字符串对象本身而不是保存在动态分配的数组中,显然在您的情况下内存仍然是可访问和未修改的。