如何将std :: string转换为const char *或char *?
如何将std::string
转换为char*
或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
。 有两个直接的方法来解决这个问题。
提高:: scoped_array
boost::scoped_array
会在你超出范围时为你删除内存:
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 ::vector
这是标准的方式(不需要任何外部库)。 你使用std::vector
,它完全为你pipe理内存。
std::string str; std::vector<char> writable(str.begin(), str.end()); writable.push_back('\0'); // get the char* using &writable[0] or &*writable.begin()
鉴于说…
std::string x = "hello";
从string中获取`char *`或`const char *`
如何获得一个有效的字符指针,而x
保持在范围内,并且不会进一步修改
C ++ 11简化了事物; 以下全部都可以访问相同的内部string缓冲区:
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...
所有上面的指针将保持相同的值 – 缓冲区中第一个字符的地址。 即使是一个空string也有一个“在缓冲区中的第一个字符”,因为C ++ 11保证总是保留一个额外的NUL / 0终止符字符后明确分配的string内容(例如std::string("this\0that", 9)
将有一个持有"this\0that\0"
的缓冲区)。
鉴于以上任何一个指针:
char c = p[n]; // valid for n <= x.size() // ie you can safely read the NUL at p[x.size()]
仅用于来自&x[0]
的非const
指针:
p_x0_rw[n] = c; // valid for n <= x.size() - 1 // ie don't overwrite the implementation maintained NUL
在string的其他地方写一个NUL 不会改变string
的size()
; string
被允许包含任意数量的NULs – 它们没有被std::string
(在C ++ 03中相同)没有特别的处理。
在C ++ 03中 ,事情要复杂得多(重点突出 ):
-
x.data()
- 将
const char*
返回给标准没有要求的string内部缓冲区,以NUL结尾 (即可能是['h', 'e', 'l', 'l', 'o']
然后是未初始化的或垃圾值,意外访问具有未定义的行为 )。-
x.size()
字符可以安全读取,即x[0]
到x[x.size() - 1]
- 对于空string,你可以保证一些非NULL指针可以被安全地添加(hurray!),但是你不应该取消这个指针的引用。
-
- 将
-
&x[0]
- 对于空string,这有未定义的行为 (21.3.4)
- 例如
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
你不能调用f(&x[0], x.size());
当x.empty()
– 只使用f(x.data(), ...)
。
- 例如
- 否则,根据
x.data()
但是:- 对于非常量
x
这将产生一个非const
char*
指针; 你可以覆盖string内容
- 对于非常量
- 对于空string,这有未定义的行为 (21.3.4)
-
x.c_str()
- 将
const char*
返回给值的一个ASCIIZ(NUL-terminated)表示(即['h','e','l','l','o','\ 0'])。 - 尽pipe很less(如果有的话)select这样做的话,C ++ 03标准的措辞允许string实现自由地创build一个独立的NUL终止的缓冲区 ,从可能非NUL终止的缓冲区“暴露”
x.data()
和&x[0]
-
x.size()
+ 1个字符可以安全读取。 - 即使对于空string(['\ 0'])也能保证安全。
- 将
获取外部法律索引的后果
无论你如何获得一个指针,你都不能从指针中进一步访问存储器中的内容,而不是上面保证的字符。 尝试这样做的方式有未定义的行为 ,即使对于读取,也有非常现实的应用程序崩溃和垃圾结果的可能性,另外还有批量数据,堆栈损坏和/或写入安全漏洞。
这些指针何时失效?
如果调用一些修改string
成员函数或保留更多的容量,任何上述方法事先返回的任何指针值都将失效 。 您可以再次使用这些方法来获取另一个指针。 (规则与迭代器到string
s相同)。
另请参见如何获取字符指针即使在x
离开作用域后仍然有效,或者在下面进一步修改 ….
那么,哪个更好用?
从C ++ 11开始,对ASCIIZ数据使用.c_str()
,对“二进制”数据使用.data()
(下面进一步解释)。
在C ++ 03中,除非确定.data()
足够,否则使用.c_str()
,并且在&x[0]
之上select.data()
,因为它对于空string是安全的。
…尝试了解程序足够使用data()
适当时,或者你可能会犯其他错误…
由.c_str()
保证的ASCII NUL'\ 0'字符被许多函数用作表示相关和安全访问数据结束的.c_str()
值。 这适用于像fstream::fstream(const char* filename, ...)
这样的仅用于C ++的函数fstream::fstream(const char* filename, ...)
以及用strchr()
和printf()
等C函数共享。
鉴于C ++ 03的.c_str()
对返回缓冲区的保证是一个超集.data()
的,你总是可以安全地使用.c_str()
,但是人们有时不会这样做,因为:
- 使用
.data()
与其他程序员通信,阅读源代码,数据不是ASCIIZ(而是使用string来存储数据块(有时甚至不是真正的文本)),重新传递给另一个函数,将其视为一个“二进制”数据块。 这可以是确保其他程序员的代码更改继续正确处理数据的关键洞察力。 - 仅限于C ++ 03:您的
string
实现将需要做一些额外的内存分配和/或数据复制,以便准备NUL终止的缓冲区
作为一个进一步的提示,如果一个函数的参数需要( const
) char*
但不要求得到x.size()
,那么函数可能需要一个ASCIIZinput,所以.c_str()
是一个不错的select要知道文本在哪里终止,所以如果它不是一个单独的参数,它只能是一个像长度前缀或标记或一些固定的期望长度的约定)。
如何获得一个字符指针,即使在x
离开作用域后仍然有效,或者进一步修改
您需要将 string
x
的内容复制到x
之外的新存储区域。 这个外部缓冲区可能在许多地方,例如另一个string
或字符数组variables,由于处于不同的范围(例如命名空间,全局,静态,堆,共享内存,内存映射),它可能会或可能不会有与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 (eg 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*
,以及如何创build一个独立于原始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
variables已经离开了作用域 - 对于不同的
std::string
实现(例如,STLport和编译器本地)编译/链接的共享对象的某些项目可以将数据作为ASCIIZ传递以避免冲突
- 注意不要返回一个可能被调用者解除引用的指针,该指针指向的本地
对const char *
使用.c_str()
方法。
你可以使用&mystring[0]
来获得一个char *
指针,但是有几个问题:你不一定会得到一个零终止的string,你将不能改变string的大小。 你特别要小心,不要在string末尾添加字符,否则你会得到一个缓冲区溢出(可能会崩溃)。
在C ++ 11之前,并不能保证所有的字符都是同一个连续缓冲区的一部分,但实际上所有已知的std::string
实现都是这样工作的。 请参阅“&s [0]”是否指向std :: string中的连续字符? 。
请注意,许多string
成员函数将重新分配内部缓冲区并使您可能保存的任何指针失效。 最好立即使用它们,然后丢弃。
C ++ 17
C ++ 17 (即将发布的标准)改变了模板basic_string
的概要,添加了一个非const的data()
重载data()
:
charT* data() noexcept;
返回:在[0,size()]中,每个i的p + i ==&运算符的指针p。
CharT const *
来自std::basic_string<CharT>
std::string const cstr = { "..." }; char const * p = cstr.data(); // or .c_str()
CharT *
来自std::basic_string<CharT>
std::string str = { "..." }; char * p = str.data();
C ++ 11
CharT const *
来自std::basic_string<CharT>
std::string str = { "..." }; str.c_str();
CharT *
来自std::basic_string<CharT>
从C ++ 11开始,标准说:
basic_string
对象中的char类对象应连续存储。 也就是说,对于任何basic_string
对象,标识&*(s.begin() + n) == &*s.begin() + n
应该适用于&*(s.begin() + n) == &*s.begin() + n
的所有值,使得0 <= n < s.size()
。
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
如果
pos < size()
,则返回*(begin() + pos)
,否则引用CharT
typesCharT
的对象。 参考值不得修改。
const charT* c_str() const noexcept;
const charT* data() const noexcept;
返回:对于
[0,size()]
每个i
,指针p使得p + i == &operator[](i)
。
有很多可能的方法来获得一个非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();
临
- 简单
- 自动内存处理
- dynamic
缺点
- 需要string复制
3.如果N
是编译时间常量(足够小) std::array<CharT, 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());
临
- 简单
- 堆栈内存处理
缺点
- 静态的
- 需要string复制
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]);
临
- 小内存占用
- 自动删除
- 简单
缺点
- 需要string复制
- 静态(dynamic使用需要更多的代码)
- less于向量或数组的特征
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; }
临
- 最大“控制”
精读
- 需要string复制
- 对错误的最大责任/易感性
- 复杂
我正在使用一个有很多函数的API作为一个char*
input。
我创造了一个小class来面对这样的问题,我实施了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
类,因为它正在创build一个现有string的深度唯一副本( DeepString
不可复制)。
只要看到这个:
string str1("stackoverflow"); const char * str2 = str1.c_str();
但是,请注意,这将返回一个const char *
。对于char *
,请使用strcpy
将其复制到另一个char
数组中。
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
尝试这个
std::string s(reinterpret_cast<const char *>(Data), Size);