c++ - 如何将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()] = '';//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()] = '';//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('');

//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("thisthat", 9) 将有一个保存 "thisthat"的缓冲区。

给定上述任何一个指针:


char c = p[n];//valid for n <= x.size()
//i.e. you can safely read the NUL at p[x.size()]

在仅对非 const pointer:


p_x0_rw[n] = c;//valid for n <= x.size() - 1
//i.e. don't overwrite the implementation maintained NUL

编写一个对称零字符串中的其他地方并不 更改 stringsize()string 都不允许包含任何数量的NULs - 它们是给了特殊处理的,通过 std::string ( C++03中相同) 。

C++03 中,事情的复杂性相当复杂:

  • x.data()

    • 用一个对称零 ( const char* 返回到内部的字符串缓冲区 这种方式不是所要求的标准来 conclude. 可能是 ['h', 'e', 'l', 'l', 'o'] 后面是uninitialised或者垃圾值,在无意中访问它们,但有未定义的行为 。
      • x.size() 字符可以安全读取,换句话说,x[0] 通过 x[x.size() - 1]
      • 对于空字符串,你都一定有些non-NULL指针 0向它的添加( hurray ),但你不应该可以安全地进行解引用这个指针。
  • &x[0]

    • 是否有空字符串这个有未定义的行为 ( 21.3 。4 )
      • e.g 。给定 f(const char* p, size_t n) { if (n == 0) return;.. .whatever... } 则不得调用 f(&x[0], x.size());x.empty() - 只要使用 f(x.data(),.. .)
    • 否则,按 x.data(),但:
      • 对于非 constx,这将生成非 constchar* 指针;你可以覆盖字符串内容
  • x.c_str()

    • const char* 返回到值的ASCIIZ ( NUL-terminated ) 表示形式( 例如 。 ['h','e','l','l','o',''] ) 。
    • 虽然在C++03却几乎没有什么互动的实现选择了这样做,标准是态度发布,以允许自由地创建一个字符串实现 独特NUL-terminated缓冲区 动态地由 x.data()&x[0]"公开",从潜在non-NUL终止缓冲区
    • x.size() + 1字符可以安全读取。
    • 即使是空字符串,也保证安全。

访问外部法律索引的后果

无论你获得哪种指针,你都不能像上描述中保证的那样,进一步从指针访问内存。 试图做到这一点有未定义的行为,有一个很现实的几率应用程序崩溃和垃圾结果哪怕只是读,并另外批发数据。堆栈损坏和/或者安全漏洞的,写。

这些指针什么时候失效?

如果调用一些函数,该函数将修改 stringstring 成员或者保留更多容量,任何通过上述任一方法返回指针值预先失效性 。 你可以再次使用这些方法来获取另一个指针。 ( 规则与 string的迭代器相同) 。

另请参见如何获取一个字符指针有效甚至当 x 离开作用域或者被修改进一步如下。。

所以,这是要使用更好?

从 C++11,对ASCIIZ数据使用 .c_str(),对于"二进制"数据( 进一步解释) 使用 .data()

在C++03中,使用 .c_str(),除非确定 .data() 足够了,并且更喜欢 .data() 上的,因为它对于空字符串是安全的。

- - 试图理解这个程序,可以通过适当的时候使用 data(),或者就有可能使得其他的错误- -

雅舒对称零保证通过 .c_str()''字符由很多功能使用相关和safe-to-access数据。作为一个哨兵值,表示结束。 这既适用于C++-only函数,也适用于 fstream::fstream(const char* filename,.. .) 和shared-with-C函数,比如 strchr(),和 printf()

给定的.data() c++03 .c_str(),保证它的面积大约返回的缓冲区是一个 super-set,你们随时都可以安全地使用 .c_str(),但有时人们不因为的消息

  • 使用 .data() 与其他读取数据的程序员通信,这些源代码不是 ASCIIZ ( 相反,你正在使用字符串来存储数据块( 有时甚至不是真正的文本) ),或者你将它传递给另一个将它作为"二进制"数据块的函数。 这对于确保其他程序员的代码更改继续正确处理数据非常重要。
  • C++03仅:string 实现可能需要做一些额外的内存分配和/或者数据复制以准备NUL终止的缓冲区

作为进一步的提示,如果函数的参数需要( const ) char* 但不坚持获取 x.size(),那么函数可能需要ASCIIZ输入,所以 .c_str() 是一个不错的选择( 函数需要知道文本的终止位置,所以如果它不是一个单独的参数,它只能是一个像length-prefix或者sentinel或者某些固定预期长度的约定) 。

如何在 x 离开作用域或者进一步修改后获取字符指针有效

你将需要 复制的内容 stringxx 到新的内存区域。 这个外部缓冲区可能在许多地方,比如另一个 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 =="abcd" -> 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.size());//without the NUL
std::vector<char> old_x(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->Hel)
char y[N + 1];
strncpy(y, x.c_str(), N);//copy at most N, zero-padding if shorter
y[N] = '';//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* 生成

上面it,是这样,你已经看到了如何获取一个( 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* ( 可能是由于历史原因- 使用客户端),或者出于C 兼容性,你不想返回一个 std::string,但是想要复制你的string 数据到调用者
    • 注意不要返回一个指针,可能会被取消引用的人从一个本地 string 变量来,这个指针尖已经离开作用域后,
    • 某些项目为不同的std::string 实现编译/链接了共享对象( 例如。 STLport和compiler-native可以作为ASCIIZ传递数据以避免冲突

使用 .c_str() 方法进行 const char *

你可以使用 &mystring[0] 获得一个 char * 指针,但有几个问题 你将不需要得到一个零终止字符串,并且你将无法更改字符串的大小。 你特别需要小心不要在字符串结尾添加字符,否则会得到缓冲区溢出( 和可能的崩溃) 。

在C++11之前,不能保证所有的字符都是同一个连续缓冲区的一部分,但是实际上,所有已知的std::string 实现都是这样工作的;请参见将"& [0]"指向 std::string 中的连续字符? 。

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

我正在使用一个具有大量函数的API作为输入 char*

我已经创建了一个小类来面对此类问题,我已经实现了newport的习惯用法。


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 不是 copyable ) 现有字符串的孤本。

看看这个:


string str1("stackoverflow");
const char * str2 = str1.c_str();

但是,注意这将返回一个 const char *. For char *,使用 strcpy 将它复制到另一个 char 数组。


char* result = strcpy((char*)malloc(str.length()+1), str.c_str());

...