协慌网

登录 贡献 社区

如何将 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 :: 矢量

这是标准方式(不需要任何外部库)。您使用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();
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 不会改变stringsize() ; 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(欢呼!),但你不应该取消引用该指针。
  • &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这会产生一个非const char*指针; 你可以覆盖字符串内容
  • 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或保留更多容量,则任何上述方法预先返回的任何指针值都将失效 。您可以再次使用这些方法来获取另一个指针。 (规则与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 的源代码(相反,你使用字符串存储数据块(有时甚至不是真正的文本)),或者你 '将其传递给另一个将其视为 “二进制” 数据块的函数。这可以是确保其他程序员的代码更改继续正确处理数据的重要见解。
  • 仅限 C ++ 03:您的string实现可能需要进行一些额外的内存分配和 / 或数据复制才能准备 NUL 终止缓冲区

作为进一步的提示,如果函数的参数需要( constchar*但不坚持获取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*其他原因

所以,上面你已经看到了如何得到一个( constchar* ,以及如何制作一个独立于原始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-native)可以将数据作为 ASCIIZ 传递以避免冲突

使用.c_str()方法来表示const char *

您可以使用&mystring[0]来获取char *指针,但是有一些问题:您不一定会得到一个零终止字符串,并且您将无法更改字符串的大小。您尤其要注意不要在字符串末尾添加字符,否则会出现缓冲区溢出(以及可能的崩溃)。

在 C ++ 11 之前,无法保证所有字符都是同一个连续缓冲区的一部分,但实际上所有已知的std::string实现都是这样的。看“&s [0]” 是否指向 std :: string 中的连续字符?

请注意,许多string成员函数将重新分配内部缓冲区并使您可能已保存的任何指针无效。最好立即使用它们然后丢弃。