提问



如何将std::string转换为char*const char*?

最佳参考


如果你只想将std::string传递给需要const char*的函数,你可以使用[160]


std::string str;
const char * c = str.c_str();


如果你想获得一个可写副本,比如char *,你可以这样做:


std::string str;
char * writable = new char[str.size() + 1];
std::copy(str.begin(), str.end(), writable);
writable[str.size()] = '\0'; // don't forget the terminating 0

// don't forget to free the string after finished using it
delete[] writable;


修改:请注意,上述内容并非异常安全。如果new调用和delete调用之间发生任何异常,则会泄漏内存,因为没有任何内容会自动调用delete。有两种直接的方法可以解决这个问题。


升压:: scoped_array



boost::scoped_array将在超出范围时为您删除内存:[161]


std::string str;
boost::scoped_array<char> writable(new char[str.size() + 1]);
std::copy(str.begin(), str.end(), writable.get());
writable[str.size()] = '\0'; // don't forget the terminating 0

// get the char* using writable.get()

// memory is automatically freed if the smart pointer goes 
// out of scope


的std ::矢量



这是标准方式(不需要任何外部库)。你使用std::vector,它完全管理你的记忆。[162]


std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');

// get the char* using &writable[0] or &*writable.begin()

其它参考1


鉴于......


std::string x = "hello";


从`string`

获取`char *`或`const char *`

如何在x保持在范围内并且未进一步修改时获得有效的字符指针


C ++ 11 简化了事情;以下所有内容都允许访问相同的内部字符串缓冲区:


const char* p_c_str = x.c_str();
const char* p_data  = x.data();
const char* p_x0    = &x[0];

      char* p_x0_rw = &x[0];  // compiles iff x is not const...


所有上述指针都将保存相同的值 - 缓冲区中第一个字符的地址。即使是空字符串也有缓冲区中的第一个字符,因为C ++ 11保证在显式分配的字符串内容之后始终保留额外的NUL/0终结符字符(例如std::string("this\0that", 9)将有一个缓冲区保持std::string("this\0that", 9) 36]])。


给出以上任何指针:


char c = p[n];   // valid for n <= x.size()
                 // i.e. you can safely read the NUL at p[x.size()]


仅适用于来自&x[0]的非const指针:


p_x0_rw[n] = c;  // valid for n <= x.size() - 1
                 // i.e. don't overwrite the implementation maintained NUL


在字符串中的其他位置写入NUL 不更改strings size(); strings允许包含任意数量的NUL - 它们被赋予std::string没有特殊处理(在C ++ 03中也是如此)。


C ++ 03 中,事情要复杂得多(关键差异 突出显示 ):



  • x.data()



    • const char*返回到字符串的内部缓冲区 ,标准不需要以NUL结尾 (即可能['h', 'e', 'l', 'l', 'o']]]后跟未初始化或垃圾值,意外访问未定义行为)。


      • x.size()字符可以安全阅读,即x[0]x[x.size() - 1]

      • 对于空字符串,你可以保证一些非空指针可以安全地添加0(欢呼!),但是你不应该取消引用那个指针。



  • &x[0]



    • 表示空字符串有未定义的行为 (21.3.4)


      • e.g。给f(const char* p, size_t n) { if (n == 0) return; ...whatever... }你不能在x.empty()时调用f(&x[0], x.size()); - 只需使用f(x.data(), ...)


    • 否则,根据x.data()但是:


      • for non - const x这会产生一个非const char*指针;你可以覆盖字符串内容



  • x.c_str()



    • const char*返回到值的ASCIIZ(NUL终止)表示(即[[h,e,l,l,o,\\ 0]])

    • 虽然很少有任何实现选择这样做,但C ++ 03标准的措辞是允许字符串实现自由创建 不同的NUL终止缓冲区 动态,来自x.data()&x[0]
    • 暴露的潜在非NUL终止缓冲区
    • x.size() + 1个字符可以安全阅读。

    • 即使是空字符串也保证安全([[\\ 0]])。




访问外部法律索引的后果



无论您获得指针的方式如何,您都不能从指针进一步访问内存,而不是上面描述中保证的字符。尝试这样做有未定义的行为,即使对于读取,也存在非常真实的应用程序崩溃和垃圾结果的可能性,此外还有批量数据,堆栈损坏和/或写入的安全漏洞。


这些指针什么时候失效?



如果你调用某个string成员函数修改string或保留更多容量,任何上述方法预先返回的任何指针值无效。您可以再次使用这些方法来获取另一个指针。 (规则与string s中的迭代器相同。


另请参阅如果x离开作用域或在下面进一步修改,如何使字符指针有效....


那么,使用哪个更好?



从C ++ 11开始,对[[ASCIIZ数据使用.c_str(),对二进制数据使用.data()(下面进一步说明)。


在C ++ 03中,使用.c_str()除非确定.data()足够,并且.data()优于&x[0],因为它对空字符串是安全的....


...尝试在适当的时候理解程序足以使用data(),否则你可能会犯其他错误......


.c_str()保证的ASCII NUL\\ 0字符被许多函数用作表示相关和安全访问数据结束的标记值。这适用于C ++ - 仅像fstream::fstream(const char* filename, ...)这样的函数和与strchr()printf()共享的C函数。


鉴于C ++ 03s .c_str()对返回缓冲区的保证是.data()的超集,你可以随时安全地使用.c_str(),但人们有时候不会这样做因为:



  • 使用.data()与其他程序员沟通,阅读数据不是ASCIIZ的源代码(相反,你使用字符串来存储数据块(有时甚至不是真正的文本)),或者你将它传递给另一个将其视为二进制数据块的函数。这可以成为确保其他程序员代码更改继续正确处理数据的重要见解。

  • 仅限C ++ 03:为了准备NUL终止缓冲区,您的string实现可能需要进行一些额外的内存分配和/或数据复制



作为进一步的提示,如果函数的参数需要(const)char*但不坚持获得x.size(),函数可能需要一个ASCIIZ输入,所以.c_str()是一个很好的选择(函数需要知道文本以某种方式终止的位置,所以如果它不是一个单独的参数,它只能是一个像长度前缀或标记或一些固定的约定预期长度)。


如果x离开作用域或进一步修改

,如何使字符指针有效

你需要 复制 string x的内容到x之外的新内存区域。这个外部缓冲区可以在许多地方,如另一个string或字符数组变量,由于处于不同的范围(例如命名空间,全局,静态,堆,共享),它可能会或可能不会具有与x不同的生命周期内存,内存映射文件)。


要将std::string x中的文本复制到独立的字符数组中:


// USING ANOTHER STRING - AUTO MEMORY MANAGEMENT, EXCEPTION SAFE
std::string old_x = x;
// - old_x will not be affected by subsequent modifications to x...
// - you can use `&old_x[0]` to get a writable char* to old_x's textual content
// - you can use resize() to reduce/expand the string
//   - resizing isn't possible from within a function passed only the char* address

std::string old_x = x.c_str(); // old_x will terminate early if x embeds NUL
// Copies ASCIIZ data but could be less efficient as it needs to scan memory to
// find the NUL terminator indicating string length before allocating that amount
// of memory to copy into, or more efficient if it ends up allocating/copying a
// lot less content.
// Example, x == "ab\0cd" -> old_x == "ab".

// USING A VECTOR OF CHAR - AUTO, EXCEPTION SAFE, HINTS AT BINARY CONTENT, GUARANTEED CONTIGUOUS EVEN IN C++03
std::vector<char> old_x(x.data(), x.data() + x.size());       // without the NUL
std::vector<char> old_x(x.c_str(), x.c_str() + x.size() + 1);  // with the NUL

// USING STACK WHERE MAXIMUM SIZE OF x IS KNOWN TO BE COMPILE-TIME CONSTANT "N"
// (a bit dangerous, as "known" things are sometimes wrong and often become wrong)
char y[N + 1];
strcpy(y, x.c_str());

// USING STACK WHERE UNEXPECTEDLY LONG x IS TRUNCATED (e.g. Hello\0->Hel\0)
char y[N + 1];
strncpy(y, x.c_str(), N);  // copy at most N, zero-padding if shorter
y[N] = '\0';               // ensure NUL terminated

// USING THE STACK TO HANDLE x OF UNKNOWN (BUT SANE) LENGTH
char* y = alloca(x.size() + 1);
strcpy(y, x.c_str());

// USING THE STACK TO HANDLE x OF UNKNOWN LENGTH (NON-STANDARD GCC EXTENSION)
char y[x.size() + 1];
strcpy(y, x.c_str());

// USING new/delete HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = new char[x.size() + 1];
strcpy(y, x.c_str());
//     or as a one-liner: char* y = strcpy(new char[x.size() + 1], x.c_str());
// use y...
delete[] y; // make sure no break, return, throw or branching bypasses this

// USING new/delete HEAP MEMORY, SMART POINTER DEALLOCATION, EXCEPTION SAFE
// see boost shared_array usage in Johannes Schaub's answer

// USING malloc/free HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = strdup(x.c_str());
// use y...
free(y);


要从string

生成char*const char*的其他原因

所以,上面你已经看过如何获得(const)char*,以及如何制作独立于原string的文本副本,但是你可以用它做?一些随机的例子......



  • 给C代码访问C ++ string的文本,如printf("x is '%s'", x.c_str());

  • x的文本复制到函数调用者指定的缓冲区(例如strncpy(callers_buffer, callers_buffer_size, x.c_str())),或用于设备I/O的易失性存储器(例如for (const char* p = x.c_str(); *p; ++p) *p_device = *p;)

  • x的文本附加到已包含某些ASCIIZ文本的字符数组(例如strcat(other_buffer, x.c_str())) - 注意不要超出缓冲区(在许多情况下,您可能需要使用strncat]])

  • 从函数返回const char*char*(可能是出于历史原因 - 客户端使用您现有的API - 或者对于C兼容性,您不想返回std::string ,但确实想要为调用者复制string的数据


    • 注意不要在指针指向的本地string变量已经离开范围之后返回可能被调用者取消引用的指针

    • 为不同的std::string实现编译/链接的共享对象的一些项目(例如STLport和编译器本机)可以将数据作为ASCIIZ传递以避免冲突



其它参考2


使用const char *.c_str()方法。


您可以使用&mystring[0]来获取char *指针,但是有几个问题:您不会得到一个零终止字符串,并且您无法更改字符串的尺寸。你尤其要注意不要在字符串末尾添加字符,否则你会得到一个缓冲区溢出(并且可能会崩溃)。


在C ++ 11之前,无法保证所有字符都是同一个连续缓冲区的一部分,但实际上std::string的所有已知实现都是这样工作的;看& s

其它参考3

是否指向std :: string中的连续字符?


请注意,许多string成员函数将重新分配内部缓冲区并使您可能已保存的任何指针无效。最好立即使用它们然后丢弃。

其它参考4


C ++ 17



C ++ 17 (即将推出的标准)更改了模板basic_string的概要,添加了data()的非常量重载:



  charT* data() noexcept;

  
  返回:指针p,使得[[0,size()]]中的每个i的p + i ==&运算符。






来自std::basic_string<CharT>

CharT const *

std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()


来自std::basic_string<CharT>

CharT *

std::string str = { "..." };
char * p = str.data();


C ++ 11



来自std::basic_string<CharT>

CharT const *

std::string str = { "..." };
str.c_str();


来自std::basic_string<CharT>

CharT *

从C ++ 11开始,标准说:



  

      
  1. basic_string对象中的char状对象应连续存储。也就是说,对于任何basic_string对象s,身份&*(s.begin() + n) == &*s.begin() + n应适用于n的所有值,以便0 <= n < s.size()

  2.   

  
  

  
  

      
  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

      
      返回:*(begin() + pos) if pos < size(),否则引用CharT类型的对象,其值为CharT();参考值不得修改。

  2.   

  
  

  
  

      
  1. const charT* c_str() const noexcept;
    const charT* data() const noexcept;

      
      返回:指针p,[0,size()][0,size()]中的每个i

  2.   



有可分割的方法来获取非const字符指针。


1。使用C ++ 11

的连续存储

std::string foo{"text"};
auto p = &*foo.begin();






  • 简单

  • 快速(仅涉及无复制的方法)



缺点



  • 最终'\0'不得更改/不一定是非常规内存的一部分。



2。使用std::vector<CharT>



std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();






  • 简单

  • 自动内存处理

  • 动态



缺点



  • 需要字符串副本



3。如果N是编译时间常量(并且足够小)

,请使用std::array<CharT, N>

std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());






  • 简单

  • 堆栈内存处理



缺点



  • 静态

  • 需要字符串副本



4。具有自动存储删除的原始内存分配



std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);






  • 占用内存不足

  • 自动删除

  • 简单



缺点



  • 需要字符串副本

  • 静态(动态使用需要更多代码)

  • 比矢量或数组少的功能



5。手动处理的原始内存分配



std::string foo{ "text" };
char * p = nullptr;
try
{
  p = new char[foo.size() + 1u];
  std::copy(foo.data(), foo.data() + foo.size() + 1u, p);
  // handle stuff with p
  delete[] p;
}
catch (...)
{
  if (p) { delete[] p; }
  throw;
}






  • 最大控制



N



  • 需要字符串副本

  • 最大责任/错误易感性

  • 复合


其它参考5


我正在使用一个带有很多函数的API作为输入char*


我创建了一个小班来面对这类问题,我已经实现了RAII成语。


class DeepString
{
        DeepString(const DeepString& other);
        DeepString& operator=(const DeepString& other);
        char* internal_; 

    public:
        explicit DeepString( const string& toCopy): 
            internal_(new char[toCopy.size()+1]) 
        {
            strcpy(internal_,toCopy.c_str());
        }
        ~DeepString() { delete[] internal_; }
        char* str() const { return internal_; }
        const char* c_str()  const { return internal_; }
};


你可以用它作为:


void aFunctionAPI(char* input);

//  other stuff

aFunctionAPI("Foo"); //this call is not safe. if the function modified the 
                     //literal string the program will crash
std::string myFoo("Foo");
aFunctionAPI(myFoo.c_str()); //this is not compiling
aFunctionAPI(const_cast<char*>(myFoo.c_str())); //this is not safe std::string 
                                                //implement reference counting and 
                                                //it may change the value of other
                                                //strings as well.
DeepString myDeepFoo(myFoo);
aFunctionAPI(myFoo.str()); //this is fine


我已经调用了类DeepString,因为它正在创建一个现有字符串的深度和唯一副本(DeepString不可复制)。

其它参考6


char* result = strcpy((char*)malloc(str.length()+1), str.c_str());

其它参考7


看看这个:


string str1("stackoverflow");
const char * str2 = str1.c_str();


但请注意,这将返回const char *。对于char *,使用strcpy将其复制到另一个char数组中。

其它参考8


尝试这个


std::string s(reinterpret_cast<const char *>(Data), Size);