如何将 a 转换std::string
为 achar*
或 a const char*
?
10 回答
如果您只想将 a 传递给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
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::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简化了事情;以下所有内容都可以访问相同的内部字符串缓冲区:
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
和 from &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
's size()
; string
's 可以包含任意数量的 NUL - 它们没有被给予特殊处理std::string
(在 C++03 中相同)。
在C++03中,事情要复杂得多(突出显示的关键差异):
x.data()
- 返回到标准不要求以 NUL 结束
const char*
的字符串的内部缓冲区(即,后面可能是未初始化或垃圾值,意外访问具有未定义的行为)。['h', 'e', 'l', 'l', 'o']
x.size()
字符可以安全阅读,即x[0]
通过x[x.size() - 1]
- 对于空字符串,您可以保证某些非 NULL 指针可以安全地添加到其中 0 (欢呼!),但您不应该取消引用该指针。
- 返回到标准不要求以 NUL 结束
&x[0]
- 对于空字符串,这具有未定义的行为(21.3.4)
- 例如,鉴于
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
this 产生一个非const
char*
指针;你可以覆盖字符串内容
- 对于非
- 对于空字符串,这具有未定义的行为(21.3.4)
x.c_str()
- 返回值
const char*
的 ASCIIZ(以 NUL 结尾)表示(即 ['h', 'e', 'l', 'l', 'o', '\0'])。 - 尽管很少有实现选择这样做,但 C++03 标准的措辞是允许字符串实现自由地从潜在的非 NUL 终止缓冲区中创建一个不同的 NUL 终止缓冲区 ,
x.data()
由和&x[0]
x.size()
+ 1 个字符可以安全阅读。- 即使对于空字符串 (['\0']) 也能保证安全。
- 返回值
访问外部法律索引的后果
无论您以何种方式获得指针,您都不能从指针访问超出上述描述中保证的字符的内存。这样做的尝试具有未定义的行为,即使对于读取,应用程序崩溃和垃圾结果的可能性也非常大,此外还有大量数据、堆栈损坏和/或写入的安全漏洞。
这些指针什么时候失效?
如果您调用某个string
修改或保留更多容量的成员函数,string
则上述任何方法预先返回的任何指针值都是无效的。您可以再次使用这些方法来获取另一个指针。(规则与 s 中的迭代器相同string
)。
另请参阅如何在x
离开范围或在下面进一步修改后使字符指针有效......
那么,哪个更好用呢?
从 C++11 开始,.c_str()
用于 ASCIIZ 数据和.data()
“二进制”数据(下文进一步解释)。
在 C++03 中,.c_str()
除非确定这.data()
是足够的,否则使用,并且更喜欢它.data()
,&x[0]
因为它对空字符串是安全的......
...尝试充分理解该程序以data()
在适当时使用,否则您可能会犯其他错误...
许多函数使用ASCII NUL '\0' 字符.c_str()
作为标记值,表示相关和安全访问数据的结束。这适用于仅 C++ 的函数(如 say fstream::fstream(const char* filename, ...)
)和与 C 共享的函数(如strchr()
, 和printf()
.
鉴于 C++03.c_str()
对返回缓冲区的 ' 保证是 ' 的超集.data()
,您始终可以安全地使用.c_str()
,但人们有时不会这样做,因为:
- using
.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 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);
想要一个char*
或const char*
从一个生成的其他原因string
所以,上面你已经看到了如何获得一个 ( 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
) - 从函数返回 a
const char*
或char*
从函数返回(可能出于历史原因 - 客户端使用您现有的 API - 或者出于 C 兼容性,您不想返回 astd::string
,但确实想将您string
的数据复制到某个地方以供调用者使用)string
注意不要在指针指向的局部变量离开作用域后返回可能被调用者取消引用的指针- 一些具有为不同实现编译/链接的共享对象的项目
std::string
(例如 STLport 和编译器原生)可能会将数据作为 ASCIIZ 传递以避免冲突
使用.c_str()
方法const char *
。
您可以使用&mystring[0]
来获取char *
指针,但有几个陷阱:您不一定会获得以零结尾的字符串,并且您将无法更改字符串的大小。尤其要注意不要在字符串末尾添加字符,否则会导致缓冲区溢出(并可能崩溃)。
在 C++11 之前,不能保证所有字符都是同一个连续缓冲区的一部分,但实际上所有已知的实现都以std::string
这种方式工作;请参阅“&s[0]”是否指向 std::string 中的连续字符?.
请注意,许多string
成员函数将重新分配内部缓冲区并使您可能保存的任何指针无效。最好立即使用它们然后丢弃。
C++17
C++17(即将推出的标准)更改了模板的概要,basic_string
添加了一个非常量重载data()
:
charT* data() noexcept;
返回: 一个指针 p,使得 p + i == &operator 对于 [0,size()] 中的每个 i。
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
对象应连续存储。也就是说,对于任何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)
if ,否则为valuepos < size()
类型对象的引用;不得修改参考值。CharT
CharT()
const charT* c_str() const noexcept;
const charT* data() const noexcept;
返回: 一个指针 p 使得
p + i == &operator[](i)
对于每个i
in[0,size()]
。
有多种可能的方法来获取非常量字符指针。
1.使用C++11的连续存储
std::string foo{"text"};
auto p = &*foo.begin();
临
- 简单而简短
- 快速(不涉及复制的唯一方法)
缺点
- Final
'\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. 使用std::array<CharT, N>
ifN
是编译时间常数(并且足够小)
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;
}
临
- 最大“控制”
骗局
- 需要字符串复制
- 最大责任/对错误的敏感性
- 复杂的
我正在使用一个 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
现有字符串的深度且唯一的副本(不可复制)。
看看这个:
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());
现在从 c++ std 字符串转换为 C 样式字符串真的很容易。
为此,我们有一个string::copy
函数可以轻松地将 std 字符串转换为 C 风格的字符串。参考
string::copy
函数参数串行
- char 字符串指针
- 字符串大小,将复制多少个字符
- 位置,字符复制的开始位置
还有一点很重要,
此函数不会在操作结束时附加空字符。所以,我们需要手动放置。
代码考试如下 -
// char string
char chText[20];
// c++ string
string text = "I am a Programmer";
// conversion from c++ string to char string
// this function does not append a null character at the end of operation
text.copy(chText, text.size(), 0);
// we need to put it manually
chText[text.size()] = '\0';
// below statement prints "I am a Programmer"
cout << chText << endl;
反之亦然,从 C 样式字符串转换为 C++ 标准字符串要容易得多
我们可以通过三种方式将 C 样式字符串转换为 C++ 标准字符串
第一个是使用构造函数,
char chText[20] = "I am a Programmer";
// using constructor
string text(chText);
第二个是使用string::assign
方法
// char string
char chText[20] = "I am a Programmer";
// c++ string
string text;
// convertion from char string to c++ string
// using assign function
text.assign(chText);
第三个是赋值运算符(=),其中字符串类使用运算符重载
// char string
char chText[20] = "I am a Programmer";
// c++ string
// convertion from char string to c++ string using assignment operator overloading
string text = chText;
第三个也可以像下面这样写 -
// char string
char chText[20] = "I am a Programmer";
// c++ string
string text;
// convertion from char string to c++ string
text = chText;
假设,字符串 str="stack";
1)将字符串转换为字符*
char* s_rw=&str[0];
上面的char*(即s_rw)是可读可写的,指向需要转换成char*的字符串的基地址
2)将字符串转换为 const char *
const char* s_r=&str[0];
上面的const char*(即s_r)可读不可写,指向字符串的基地址。
试试这个
std::string s(reinterpret_cast<const char *>(Data), Size);