如何将<code>std::string转换为char*
或const char*
?
#1楼
看看这个:
string str1("stackoverflow");const char * str2 = str1.c_str();
但是请注意,这将返回const char *
。对于char *
,请使用strcpy
将其复制到另一个char
数组中。
#2楼
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
#3楼
C ++ 17
C ++ 17(即将发布的标准)更改了模板basic_string
的提要,增加了data()
的非const重载:
charT* data() noexcept;
返回:指针p,使得[0,size()]中每个i的p + i ==&operator。
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
,对于所有n
值,标识&*(s.begin() + n) == &*s.begin() + n
都应成立,以使0 <= n < s.size()
。
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
返回:
*(begin() + pos)
如果pos < size()
,否则以值CharT()
引用CharT
的对象; 参考值不得修改。
const charT* c_str() const noexcept;
const charT* data() const noexcept;
返回:指针p,使得
[0,size()]
每个i
具有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();
专业版
简单 自动内存处理 动态
缺点
需要字符串复制
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());
专业版
简单 堆栈内存处理
缺点
静态的 需要字符串复制
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 pdelete[] p;}catch (...){if (p) { delete[] p; }throw;}
专业版
最大的“控制”
骗局
需要字符串复制 对错误的最大责任/敏感性 复杂
#4楼
对const char *
使用.c_str()
方法。
您可以使用&mystring[0]
获取char *
指针,但是有一些陷阱:您不一定会获得零终止的字符串,也无法更改字符串的大小。 您尤其要注意不要在字符串末尾添加字符,否则会导致缓冲区溢出(并可能崩溃)。
在C ++ 11之前,无法保证所有字符都将是同一连续缓冲区的一部分,但实际上,所有已知的std::string
实现都以这种方式工作; 请参见“&s [0]”是否指向std :: string中的连续字符? 。
请注意,许多string
成员函数将重新分配内部缓冲区,并使您可能保存的所有指针无效。 最好立即使用它们,然后丢弃。
#5楼
如果只想将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 itdelete[] writable;
编辑:请注意,以上内容并非异常安全。 如果在new
呼叫和delete
呼叫之间发生任何故障,您将泄漏内存,因为没有任何东西会自动为您呼叫delete
。 有两种直接的方法可以解决此问题。
boost :: 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
,它将完全为您管理内存。
std::string str;std::vector<char> writable(str.begin(), str.end());writable.push_back('\0');// get the char* using &writable[0] or &*writable.begin()
#6楼
给出说...
std::string x = "hello";
从“字符串”中获取“ char *”或“ const char *”
如何获得在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
和&x[0]
:
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不会更改string
的size()
;string
可以包含任意数量的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的一些非NULL指针(欢呼!),但是您不应该取消引用该指针。
&x[0]
对于空字符串,它具有未定义的行为(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()
但: 对于非const
x
这将产生一个非const
char*
指针; 您可以覆盖字符串内容
x.c_str()
将const char*
返回到值的ASCIIZ(NUL终止)表示形式(即['h','e','l','l','o','\\ 0'])。 尽管很少有实现选择这样做,但是C ++ 03 Standard的措辞是允许字符串实现自由地从x.data()
公开的可能非NUL终止的缓冲区中动态创建独特的NUL终止的缓冲区。x.data()
和&x[0]
x.size()
+ 1个字符可以安全阅读。 即使对于空字符串(['\\ 0'])也保证安全。
访问外部法律索引的后果
无论采用哪种方式获取指针,都不能比上述说明中保证的字符距离指针更远。 尝试执行此操作具有不确定的行为,即使是读取操作,也很有可能发生应用程序崩溃和垃圾结果,此外还可能批发数据,堆栈损坏和/或写入的安全漏洞。
这些指针什么时候失效?
如果调用某个string
成员函数来修改string
或保留更多容量,则上述任何一种方法事先返回的任何指针值都将无效。 您可以再次使用这些方法来获取另一个指针。 (规则与对string
s进行迭代的规则相同)。
另请参见即使在x
离开范围或在下面进一步修改后,如何使字符指针仍然有效。
那么,哪个更好用?
从C ++ 11开始,对于ASCIIZ数据使用.c_str()
,对于“二进制”数据使用.data()
(在下面进一步解释)。
在C ++ 03中,除非确定.data()
足够,否则使用.c_str()
,.c_str()
&x[0]
更喜欢.data()
,因为它对于空字符串是安全的。
...试图了解程序足以在适当的时候使用data()
,否则您可能会犯其他错误...
.c_str()
保证的ASCII NUL'\\ 0'字符被许多函数用作指示相关和可安全访问数据结尾的.c_str()
值。 这适用于仅C ++的函数,例如fstream::fstream(const char* filename, ...)
以及与C共享的函数,例如strchr()
和printf()
。
鉴于C ++ 03的.c_str()
关于返回的缓冲区的保证是.data()
的超集,因此您始终可以安全地使用.c_str()
,但是人们有时却不会这样做,因为:
使用.data()
可以与其他读取源代码的程序员通信,即该数据不是ASCIIZ(而是您使用字符串存储数据块(有时甚至不是真正的文本)),或者将其传递给另一个将其视为“二进制”数据块的函数。 这对于确保其他程序员的代码更改继续正确处理数据至关重要。 仅限于C ++ 03:您的string
实现很有可能需要做一些额外的内存分配和/或数据复制,以准备NUL终止的缓冲区
进一步提示,如果函数的参数需要(const
)char*
但不坚持获取x.size()
,则该函数可能需要ASCIIZ输入,因此.c_str()
是一个不错的选择(函数需要知道文本在何处终止,因此,如果它不是一个单独的参数,则只能是一个约定,例如长度前缀或前哨或某个固定的预期长度。
即使在x
离开范围或进一步修改后,如何使字符指针有效
你需要的内容复制string
x
到一个新的存储区域外x
。 此外部缓冲区可能在许多地方,例如另一个string
或字符数组变量,由于处于不同的范围(例如,名称空间,全局,静态,堆,共享内存,映射的内存),它的生存期可能与x
相同或不同。文件)。
要将文本从std::string x
复制到一个独立的字符数组中:
// USING ANOTHER STRING - AUTO MEMORY MANAGEMENT, EXCEPTION SAFEstd::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* addressstd::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++03std::vector<char> old_x(x.data(), x.data() + x.size()); // without the NULstd::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 shortery[N] = '\0';// ensure NUL terminated// USING THE STACK TO HANDLE x OF UNKNOWN (BUT SANE) LENGTHchar* 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 SAFETYchar* 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 SAFETYchar* 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和native编译器)可以将数据作为ASCIIZ传递以避免冲突
#7楼
尝试这个
std::string s(reinterpret_cast<const char *>(Data), Size);
#8楼
我正在使用具有许多功能的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 stuffaFunctionAPI("Foo"); //this call is not safe. if the function modified the //literal string the program will crashstd::string myFoo("Foo");aFunctionAPI(myFoo.c_str()); //this is not compilingaFunctionAPI(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
是不可复制的)。