如何将std :: string转换为const char *或char *?


893

如何将std::stringa 转换为a char*或a const char*


2
代替:char * writable = new char [str.size()+ 1]; 您可以使用char writable [str.size()+ 1]; 然后,您不必担心删除可写或异常处理。

7
除非在编译时知道大小,否则不能使用str.size(),而且如果固定大小值很大,它可能会溢出堆栈。
paulm 2012年

1
char *结果= strcpy((char *)malloc(str.length()+ 1),str.c_str());
cegprakash 2014年

7
@cegprakash strcpymalloc不是真正的C ++的方式。
boycy 2014年

4
不,但是char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)会更惯用的C ++。strcpy()malloc()没有错或有问题,但是在同一代码块中使用C ++字符串和C库功能与C ++等效项似乎不一致。
boycy 2014年

Answers:


1055

如果您只想将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::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()

41
只需使用char * result = strdup(str.c_str());
贾斯珀·贝克斯

63
您可以,但是strdup不是ac或c ++标准函数,它来自posix :)
Johannes Schaub-litb

14
我通常更喜欢的是std :: vector <char> writable(str.begin(),str.end()); writable.push_back('\ 0'); char * c =&writable [0];
Johannes Schaub-litb

17
std :: copy是执行此操作的C ++方法,无需获取字符串指针。我尽量避免使用C函数。
Johannes Schaub-litb

16
从C ++ 17开始,std::string::data()现在返回CharT*而不是const CharT*。更新此答案可能是一个好主意:)
Rakete1111 '17

192

给出说...

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和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 不会更改stringsize()string允许包含任意数量的NUL-不会对它们进行特殊处理std::string(在C ++ 03中相同)。

C ++ 03中,事情要复杂得多(关键区别突出显示):

  • x.data()

    • 返回标准不要求以NULconst char*结尾的字符串的内部缓冲区(即,可能在其后跟随未初始化或垃圾值,并且意外访问具有未定义的行为)。 ['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(&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的措辞是允许字符串实现自由地创建一个独立的以NUL终止的缓冲区 ,该缓冲区是由x.data()and所“暴露”的潜在非NUL终止的缓冲区。&x[0]
    • x.size() + 1个字符可以安全阅读。
    • 即使对于空字符串(['\ 0'])也保证安全。

访问外部法律索引的后果

无论采用哪种方式获取指针,都不能比上述描述中保证的字符距离指针更远。尝试执行此操作具有不确定的行为,即使是读取操作,也很有可能发生应用程序崩溃和垃圾结果,此外,批发数据,堆栈损坏和/或写入的安全漏洞也很可能发生。

这些指针什么时候失效?

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

另请参见即使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(),但人们有时却不会这样做,因为:

  • .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);

想要char*const char*由产生的其他原因string

因此,在上面您已经了解了如何获取(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
  • 从函数返回a const char*或(char*从历史上讲-客户端正在使用您的现有API-或出于C兼容性,您不想返回a std::string,但希望将其string数据复制到调用方的某个位置)
    • 注意不要在指针指向的局部string变量离开作用域之后返回调用者可能取消引用的指针
    • 一些为不同的std::string实现而编译/链接了共享对象的项目(例如STLport和native编译器)可以将数据作为ASCIIZ传递以避免冲突

4
好一个。想要使用char *(非const)的另一个原因是要使用MPI广播。如果您不必来回复制,它看起来会更好。我本来会为字符串提供一个char * const getter。常量指针,但可编辑的字符串。尽管它可能会混淆从const char *到字符串的隐式转换...
bartgol 2014年

33

.c_str()方法用于const char *

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

在C ++ 11之前,无法保证所有字符都将是同一连续缓冲区的一部分,但是实际上,所有已知的实现std::string方式都可以用这种方式工作;请参见“&s [0]”是否指向std :: string中的连续字符?

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


1
您应该注意data()返回const char * :)的意思是&str [0],它返回一个连续的但不是必需的以null结尾的字符串。
Johannes Schaub-litb

1
@litb,啊!这就是我试图快速给出答案的结果。我过去曾经使用过您的解决方案,不知道为什么这不是我想到的第一件事。我已经编辑了答案。
Mark Ransom

2
从技术上讲,std :: string存储仅在C ++ 0x中是连续的。
MSalters

1
@MSalters,谢谢-我不知道。但是,我很难找到一个并非如此的实现。
Mark Ransom

2
char *结果= strcpy(malloc(str.length()+ 1),str.c_str());
cegprakash 2014年

21

C ++ 17

C ++ 17(即将发布的标准)更改了模板的提要,basic_string添加了以下内容的非const重载data()

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开始,该标准规定:

  1. 对象中类似char的basic_string对象应连续存储。也就是说,对于任何basic_string对象s,身份&*(s.begin() + n) == &*s.begin() + n应持的所有值n这样0 <= n < s.size()

  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    返回:*(begin() + pos)如果pos < size(),否则返回对CharT带有value 类型的对象的引用CharT();参考值不得修改。


  1. const charT* c_str() const noexcept;
    const charT* data() const noexcept;

    返回:指针p,使得in 中p + i == &operator[](i)的每个指针。i[0,size()]

有几种可能的方法来获取非const字符指针。

1.使用C ++ 11的连续存储

std::string foo{"text"};
auto p = &*foo.begin();

专业版

  • 简单而简短
  • 快速(仅包含副本的方法)

缺点

  • Final '\0'不可更改/不一定是非const内存的一部分。

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>如果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 p
  delete[] p;
}
catch (...)
{
  if (p) { delete[] p; }
  throw;
}

专业版

  • 最大的“控制”

骗局

  • 需要字符串复制
  • 对错误的最大责任/敏感性
  • 复杂

9

我正在使用具有很多功能的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现有字符串的深层且唯一的副本(不可复制)。


3
我会避免使用这种命名约定。c_str()使用的std是“ C-string”而不是“ const string”的缩写,并且str()始终返回a std::basic_string,而不是char*(例如std::stringstream::str()
bcrist

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

1
看起来花哨,但真的很难懂...简单的就是最好的IMO
纳伊姆A.马利克

4
strcpy(),malloc(),length()和c_str()是基本函数,这并不难。只是分配内存并复制。
cegprakash 2014年

5
是的,这些功能是基本功能,但您已经将它们扭曲并弯曲,使其看起来像一碗意大利面条或一个弗兰肯斯坦的衬里怪物:)
Naeem A. Malik 2014年

4
是的,这些功能是基本功能,但是……您还记得开始使用编程语言的时候吗?一些行需要进一步解释,这确实将帮助新手了解为什么例如比该答案不同或更好的原因:)
Hastur 2015年

2
@cegprakash:每当有一个malloc()时,也必须有一个free()。否则,代码将泄漏内存,答案中的解决方案也将泄漏。对于此类问题,分配内存而不至少暗示未要求重新分配是不好的做法。
Striezel '16

7

看看这个:

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

但是,请注意,这将返回const char *

对于char *,用于strcpy将其复制到另一个char数组中。


23
嗨,您发布的内容已经被多次说过,更多详细信息,以及有关5岁问题的其他答案。回答较旧的问题很好,但前提是您添加了新信息。否则,只是噪音。
Mat

7
我个人很欣赏这种简单性。
TankorSmash 2014年

-4

尝试这个

std::string s(reinterpret_cast<const char *>(Data), Size);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.