如果你只想将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_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";
如何在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)
将有一个缓冲区保存"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
从指针&x[0]
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]
&x[0]
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'])。 x.data()
和&x[0]
x.size()
+ 1 个字符可以安全阅读。 无论哪种方式获得指针,您都不能从指针进一步访问内存,而不是上面描述中保证的字符。尝试这样做具有未定义的行为 ,即使对于读取,也存在非常真实的应用程序崩溃和垃圾结果的可能性,以及批量数据,堆栈损坏和 / 或写入的安全漏洞。
如果调用某个string
成员函数来修改string
或保留更多容量,则任何上述方法预先返回的任何指针值都将失效 。您可以再次使用这些方法来获取另一个指针。 (规则与string
s 中的迭代器相同)。
另请参见如何在x
离开作用域或在下面进一步修改后使字符指针有效 ....
从 C ++ 11 开始,对于 ASCIIZ 数据使用.c_str()
,对于 “二进制” 数据使用.data()
(在下面进一步说明)。
在 C ++ 03 中,使用.c_str()
除非确定.data()
是足够的,并且更喜欢.data()
over &x[0]
因为它对空字符串是安全的....
... 尝试理解程序足以在适当的时候使用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 的源代码(相反,你使用字符串存储数据块(有时甚至不是真正的文本)),或者你 '将其传递给另一个将其视为 “二进制” 数据块的函数。这可以是确保其他程序员的代码更改继续正确处理数据的重要见解。 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);
string
生成char*
或const char*
其他原因所以,上面你已经看到了如何得到一个( const
) char*
,以及如何制作一个独立于原始string
的文本副本,但是你能用它做什么呢?一个随机的例子......
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-native)可以将数据作为 ASCIIZ 传递以避免冲突使用.c_str()
方法来表示const char *
。
您可以使用&mystring[0]
来获取char *
指针,但是有一些问题:您不一定会得到一个零终止字符串,并且您将无法更改字符串的大小。您尤其要注意不要在字符串末尾添加字符,否则会出现缓冲区溢出(以及可能的崩溃)。
在 C ++ 11 之前,无法保证所有字符都是同一个连续缓冲区的一部分,但实际上所有已知的std::string
实现都是这样的。看“&s [0]” 是否指向 std :: string 中的连续字符? 。
请注意,许多string
成员函数将重新分配内部缓冲区并使您可能已保存的任何指针无效。最好立即使用它们然后丢弃。