如何将std::string
a 转换为a char*
或a const char*
?
如果你只是想传递一个std::string
需要const char*
你可以使用的功能
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
自动为您调用.有两种直接的方法可以解决这个问题.
boost::scoped_array
超出范围时将为您删除内存:
std::string str; boost::scoped_arraywritable(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::vector
,它完全管理您的内存.
std::string str; std::vectorwritable(str.begin(), str.end()); writable.push_back('\0'); // get the char* using &writable[0] or &*writable.begin()
鉴于......
std::string x = "hello";
如何获取有效的字符指针,同时x
保留在范围内并且不会进一步修改
C++ 11简化了事情; 以下所有内容都允许访问相同的内部字符串缓冲区:
const char* p_c_str = x.c_str(); const char* p_data = x.data(); char* p_writable_data = x.data(); // for non-const x from C++17 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)
将保留缓冲区"this\0that\0"
).
给出以上任何指针:
char c = p[n]; // valid for n <= x.size() // i.e. you can safely read the NUL at p[x.size()]
仅适用于非const
指针p_writable_data
:
p_writable_data[n] = c; p_x0_rw[n] = c; // valid for n <= x.size() - 1 // i.e. don't overwrite the implementation maintained NUL
在字符串的其他地方写一个NUL 不会改变它&x[0]
的string
; size()
允许包含任意数量的NUL - 它们没有给予特殊处理string
(在C++ 03中也是如此).
在C++ 03中,事情要复杂得多(突出显示关键差异):
std::string
返回x.data()
到字符串的内部缓冲区,标准不需要以NUL结束(即可能const char*
后跟未初始化或垃圾值,其中意外访问具有未定义的行为).
['h', 'e', 'l', 'l', 'o']
字符可以安全阅读,即x.size()
通过x[0]
对于空字符串,你可以保证一些非空指针,可以安全地添加0(欢呼!),但你不应该取消引用该指针.
x[x.size() - 1]
对于空字符串,这具有未定义的行为(21.3.4)
如给&x[0]
您不能调用f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
时f(&x[0], x.size());
-只需使用x.empty()
.
否则,按照f(x.data(), ...)
:
对于non,x.data()
const
这会产生非x
const
指针; 你可以覆盖字符串内容
char*
返回值x.c_str()
的ASCIIZ(NUL终止)表示(即['h','e','l','l','o','\ 0']).
虽然几乎没有实现选择这样做,在C++ 03标准措辞,以允许字符串实现自由创造一个独特的NULL结尾的缓冲区 上飞,从潜在非NUL终止缓冲"暴露"的const char*
和x.data()
&x[0]
+ 1个字符可以安全阅读.
即使是空字符串也保证安全(['\ 0']).
无论哪种方式获得指针,您都不能从指针进一步访问内存,而不是上面描述中保证的字符.尝试这样做具有未定义的行为,即使对于读取,也存在非常真实的应用程序崩溃和垃圾结果的可能性,以及批量数据,堆栈损坏和/或写入的安全漏洞.
如果调用某个x.size()
修改string
或保留更多容量的成员函数,则任何上述方法预先返回的任何指针值都将失效.您可以再次使用这些方法来获取另一个指针.(规则与string
s中的迭代器的规则相同).
另请参见如何使字符指针有效,即使在string
叶子范围之后或在下面进一步修改 ....
从C++ 11开始,x
用于ASCIIZ数据和.c_str()
"二进制"数据(下面进一步说明).
在C++ 03,使用.data()
,除非肯定.c_str()
是足够的,而喜欢.data()
过.data()
,因为它是为空字符串安全....
...尝试&x[0]
在适当的时候理解程序,或者你可能会犯其他错误......
保证的ASCII NUL'\ 0'字符data()
被许多函数用作表示相关和安全访问数据结束的标记值.这适用于C++ -只有类似功能的说.c_str()
和类似共享与-C的功能fstream::fstream(const char* filename, ...)
,和strchr()
.
鉴于C++ 03 printf()
对返回缓冲区的保证是超集.c_str()
的,你可以随时安全地使用.data()
,但人们有时不这样做,因为:
使用.c_str()
与其他程序员沟通,阅读数据不是ASCIIZ的源代码(相反,你使用字符串来存储数据块(有时甚至不是真正的文本)),或者你将它传递给另一个将它视为"二进制"数据块的函数.这可以是确保其他程序员的代码更改继续正确处理数据的重要见解.
仅限C++ 03:您的.data()
实现可能需要进行一些额外的内存分配和/或数据复制才能准备NUL终止缓冲区
作为进一步的提示,如果一个函数的参数需要(string
)const
但不坚持获取char*
,该函数可能需要一个ASCIIZ输入,所以这x.size()
是一个很好的选择(该函数需要知道文本以某种方式终止的位置,所以如果它不是一个单独的参数,它只能是一个约定,如长度前缀或标记或一些固定的预期长度).
.c_str()
范围或进一步修改后使字符指针有效您需要将内容复制x
string
到外部的新内存区域x
.这个外部缓冲区可能在很多地方,例如另一个x
或字符数组变量,它可能会或可能没有不同的生命周期,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::vectorold_x(x.data(), x.data() + x.size()); // without the NUL std::vector 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);
std::string x
或char*
从a生成的其他原因const char*
所以,上面你已经看过如何获得一个(string
)const
,以及如何制作一个独立于原文的文本副本char*
,但是你能用它做什么呢?一个随机的例子......
给"C"代码访问C++ string
的文本,如string
将printf("x is '%s'", x.c_str());
文本复制到函数调用者指定的缓冲区(例如x
)或用于设备I/O的易失性存储器(例如strncpy(callers_buffer, callers_buffer_size, x.c_str())
)
将for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
文本追加到已经包含一些ASCIIZ文本的字符数组(例如x
) - 注意不要超出缓冲区(在很多情况下你可能需要使用strcat(other_buffer, x.c_str())
)
返回一个strncat
或const char*
一个函数(可能是出于历史原因 - 客户端使用您现有的API - 或者对于C兼容性,您不想返回一个char*
,但确实想要将您std::string
的数据复制到调用者的某个地方)
注意不要返回一个指针,该指针在指针指向的局部string
变量已经离开范围之后可能被调用者解除引用
为不同的string
实现编译/链接的一些具有共享对象的项目(例如STLport和native-native)可以将数据作为ASCIIZ传递以避免冲突
使用.c_str()
方法const char *
.
您可以使用&mystring[0]
获取char *
指针,但有几个问题:您不一定会得到一个零终止字符串,并且您将无法更改字符串的大小.您尤其要注意不要在字符串末尾添加字符,否则会出现缓冲区溢出(以及可能的崩溃).
在C++ 11之前,无法保证所有字符都是同一个连续缓冲区的一部分,但实际上所有已知的实现std::string
方式都是这样的.看"&s [0]"是否指向std :: string中的连续字符?.
请注意,许多string
成员函数将重新分配内部缓冲区并使您可能已保存的任何指针无效.最好立即使用它们然后丢弃.
C++ 17(即将推出的标准)更改了模板的概要,basic_string
添加了非常量的重载data()
:
charT* data() noexcept;
返回:指针p,使得[0,size()]中的每个i的p + i ==&operator.
CharT const *
从 std::basic_string
std::string const cstr = { "..." }; char const * p = cstr.data(); // or .c_str()
CharT *
从 std::basic_string
std::string str = { "..." }; char * p = str.data();
CharT const *
从 std::basic_string
std::string str = { "..." }; str.c_str();
CharT *
从 std::basic_string
从C++ 11开始,标准说:
对象中的char状
basic_string
对象应连续存储.也就是说,任何basic_string
对象s
,身份&*(s.begin() + n) == &*s.begin() + n
应持的所有值n
这样0 <= n < s.size()
.
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
返回:
*(begin() + pos)
ifpos < size()
,否则引用CharT
具有值的类型的对象CharT()
; 参考值不得修改.
const charT* c_str() const noexcept;
const charT* data() const noexcept;
返回:指针p,
p + i == &operator[](i)
对于每个i
in[0,size()]
.
有可分割的方法来获取非const字符指针.
std::string foo{"text"}; auto p = &*foo.begin();
临
简单而简短
快速(仅涉及无复制的方法)
缺点
最终'\0'
不得改变/不一定是非常规内存的一部分.
std::vector
std::string foo{"text"}; std::vectorfcv(foo.data(), foo.data()+foo.size()+1u); auto p = fcv.data();
临
简单
自动内存处理
动态
缺点
需要字符串副本
std::array
if N
是编译时间常量(并且足够小)std::string foo{"text"}; std::arrayfca; std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());
临
简单
堆栈内存处理
缺点
静态的
需要字符串副本
std::string foo{ "text" }; auto p = std::make_unique(foo.size()+1u); std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);
临
内存占用少
自动删除
简单
缺点
需要字符串副本
静态(动态使用需要更多代码)
比矢量或数组少的功能
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; }
临
最大'控制'
精读
需要字符串副本
最大责任/易错性
复杂
我正在使用一个带有很多函数的API作为输入a 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(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
一个现有字符串的深层唯一副本(不可复制).
看看这个:
string str1("stackoverflow"); const char * str2 = str1.c_str();
但请注意,这将返回const char *
.对于a char *
,用于strcpy
将其复制到另一个char
数组中.
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());