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

1071

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


2
你可以使用 char writable[str.size() + 1]; 代替 char * writable = new char[str.size() + 1]; 这样你就不需要担心删除writable或异常处理。 - user372024
12
如果在编译时无法确定大小,你就不能使用str.size(),而且如果固定大小的值太大,可能会导致堆栈溢出。请注意不要改变原意。 - paulm
1
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());将字符串复制到新分配的内存中,并返回指向该内存的指针。 - cegprakash
9
strcpy和malloc并不是C ++的常用方式。 - boycy
8
不,但是char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)会更符合C++的惯用方法。使用strcpy()malloc()并没有错或问题,但在同一段代码块中使用C++字符串和C库函数以及等价的C++函数看起来不太一致。 - boycy
显示剩余2条评论
11个回答

1273
如果您想将一个 std::string 传递给需要 const char * 的函数,您可以使用 .c_str() 方法:
std::string str;
const char * c = str.c_str();

如果您需要一个非const char *,请调用.data()

std::string str;
char * c = str.data();

.data() 是在 C++17 中添加的。在此之前,您可以使用 &str[0]

请注意,如果 std::stringconst 的,则 .data() 将返回 const char *,就像 .c_str() 一样。

如果字符串被销毁或重新分配内存,则指针将失效。

该指针指向以空字符终止的字符串,并且终止符不计入 str.size()。您不允许将非空字符赋值给终止符。


53
使用char *result = strdup(str.c_str());即可。 - Jasper Bekkers
75
可以这样做,但是strdup不是C或C++的标准函数,它来自于POSIX标准 :) - Johannes Schaub - litb
16
通常情况下,我倾向于使用std::vector<char> writable(str.begin(), str.end());来编写代码。这样做可以使代码更易于理解和编写。接着将 '\0' 字符加入到writable中,最后通过将指针c指向writable的首个元素,即可得到一个长度为字符串长度+1的字符数组。 - Johannes Schaub - litb
19
std::copy是C++中实现此操作的方式,而不需要获取字符串指针。我尽量避免使用C函数。 - Johannes Schaub - litb
27
自 C++17 开始,std::string::data() 现在返回一个 CharT* 而不是一个 const CharT*。更新一下这个答案可能是个好主意 :) - Rakete1111
显示剩余28条评论

225

Given say...

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和从&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可以包含任意数量的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]
    • 对于空字符串,你可以得到一些非NULL指针,可以安全地添加0(万岁!),但不应该解引用该指针。
  • &x[0]

    • 对于空字符串,这具有未定义的行为(21.3.4)。
    • 否则,与x.data()相同,但:
    • 对于非constx,它会产生一个非constchar*指针;你可以覆盖字符串内容。
  • x.c_str()

    • 返回一个指向值的ASCIZ(以NUL结尾)表示的const char*(即['h','e','l','l','o','\0'])。
    • 虽然很少有实现选择这样做,但C++03标准措辞允许字符串实现自由地创建一个不同的NUL-terminated缓冲区,从可能没有以NUL结尾的缓冲区“暴露”出来的x.data()&x[0]
    • 可以安全读取x.size()+1个字符。
    • 即使是空字符串(['\0']),也保证是安全的。

访问越界的后果

无论如何获取指针,都不能访问指针之后的内存,超出上述描述中保证存在的字符。试图这样做会导致未定义的行为,甚至对于读取而言也有非常真实的应用程序崩溃和垃圾结果的风险,此外还可能导致数据、堆栈损坏和/或安全漏洞。

这些指针什么时候失效?

如果您调用修改字符串或保留更多容量的任何字符串成员函数,则之前由上述任何方法返回的指针值将变为无效。您可以再次使用这些方法来获取另一个指针。(规则与对string进行迭代器相同)。
另请参见下面的"如何获得字符指针,即使x离开作用域或进一步修改"
那么,应该使用哪种方式更好
从C++11开始,对于ASCIIZ数据,请使用.c_str(),对于"二进制"数据,请使用.data()(下面会进一步解释)。
在C++03中,使用.c_str(),除非确定.data()是足够的,并且优先使用.data()而不是&x[0],因为它对空字符串是安全的。 ...尝试充分了解程序以在适当时使用data(),否则可能会犯其他错误....c_str()保证的ASCII NUL '\0'字符被许多函数用作表示相关和安全访问数据结束的哨兵值。这适用于仅限于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(),则该函数可能需要ASCIZ输入,因此.c_str()是一个不错的选择(函数需要以某种方式知道文本何时结束,因此,如果它不是单独的参数,则只能是类似于长度前缀或标记或某些固定的预期长度的约定)。

如何获得一个字符指针,即使在x离开作用域或进一步修改后仍然有效

您需要将string x的内容复制x之外的新内存区域中。这个外部缓冲区可以在许多地方,例如另一个string或字符数组变量中,由于处于不同的作用域(例如命名空间、全局、静态、堆、共享内存、内存映射文件),它可能具有不同的生命周期。

要将文本从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无关的文本副本,但是你可以用它做什么呢?以下是一些随机的例子...

  • 将C++ string的文本提供给"C"代码,例如: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和编译器本地)编译/链接的共享对象项目可能会将数据作为ASCIIZ传递以避免冲突

4
不错。想要一个 char(非 const)的另一个原因是为了使用 MPI 广播进行操作。如果你不必来回复制,看起来更好。我个人会提供一个 char const getter 给 string。常量指针,但可编辑字符串。虽然这可能会干扰从 const char* 到 string 的隐式转换... - bartgol

39

对于 const char *,可以使用.c_str()方法。

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

在 C++11 之前,没有保证所有字符都是同一连续缓冲区的一部分,但实际上已知的所有 std::string 实现方式都是如此;参见Does “&s[0]” point to contiguous characters in a std::string?

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


1
你应该注意到data()返回的是const char * :) 你所指的是&str[0],它返回的是一个连续的字符串,但不一定以空字符结尾。 - Johannes Schaub - litb
1
@litb,啊!这就是我试图快速回答的结果。我以前用过你的解决方案,不知道为什么它不是我首先想到的东西。我已经编辑了我的答案。 - Mark Ransom
2
从技术上讲,在C++0x中,std::string的存储将是连续的。 - MSalters
1
@MSalters,谢谢 - 我不知道那个。虽然我很难找到一个不是这种情况的实现。 - Mark Ransom
2
char* result = strcpy(malloc(str.length()+1), str.c_str()); 将字符串str复制到新分配的内存中,并将指针result指向该内存。为了确保足够的空间,需要在原始字符串长度上加1。 - cegprakash

24

C++17

C++17(即将发布的标准)改变了模板 basic_string 的概要,添加了一个非 const 的 data() 重载:

charT* data() noexcept;

返回值:指针 p ,使得对于每个 i 在 [0,size()] 区间内都有 p + i == &operator。


std::basic_string<CharT> 获取 CharT const *

std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()

std::basic_string<CharT>中的CharT *

std::string str = { "..." };
char * p = str.data();

C++11

std::basic_string<CharT> 转换为 CharT const *

std::string str = { "..." };
str.c_str();

std::basic_string<CharT>中的CharT *

从C++11开始,标准规定:

  1. basic_string对象中的类似char的对象必须连续存储。也就是说,对于任何basic_string对象s,对于所有满足0 <= n < s.size()n值,&*(s.begin() + n) == &*s.begin() + n都应该成立。

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

    返回:*(begin() + pos)(如果pos < size()),否则返回一个类型为CharT且值为CharT()的对象引用;不得修改所引用的值。


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

    返回一个指针p,使得对于每个满足i ∈ [0,size()]ip + i == &operator[](i)

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

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

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

优点

  • 简单明了
  • 快速(是唯一一个不涉及复制的方法)

缺点

  • 不能更改最终的 '\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. 如果N是编译时常量(且足够小),请使用std::array<CharT,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]);

优点

  • 占用内存小
  • 自动删除
  • 简单易用

缺点

  • 需要复制字符串
  • 静态(动态使用需要更多的代码)
  • 比vector或array功能少

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;
}

优点:

  • 最大程度的“控制”

缺点:

  • 需要字符串复制
  • 最大的责任/错误风险
  • 复杂

10

我正在使用一个包含很多以char*作为输入的函数的API。

为了解决这种问题,我创建了一个小类,并实现了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
我会尽力进行翻译,保留原意并将其修饰以使其更加易懂。以下是需要翻译的内容:我建议避免使用这种命名规范。在std中,“c_str()代表"C-string"而不是“const string”,而str()始终返回std::basic_string类型,而不是char *类型(例如std :: stringstream :: str()`)。 - bcrist

10

看看这个:

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

请注意,这将返回一个const char *

如果需要一个char *,请使用strcpy将其复制到另一个char数组中。


27
你发布的内容已经在其他回答这个五年前问题的答案中多次提到并详细阐述了。回答旧问题是可以的,但只有当你添加新信息时才可以。否则,就是无用的噪音。 - Mat
9
就我个人而言,我很欣赏简单。 - TankorSmash

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

2
看起来很花哨,但真的很难理解... 在我看来,简单才是最好的。 - Naeem A. Malik
4
strcpy()、malloc()、length() 和 c_str() 是基本函数,它们并不难。只需要分配内存和复制即可。 - cegprakash
6
是的,这些功能很基本,但你已经把它们扭曲和弯曲,看起来像一碗意大利面条或一个单行的弗兰肯斯坦怪物 :) - Naeem A. Malik
4
是的,这些功能很基础,但是你还记得开始学习编程语言时的情况吗?再多解释几行会帮助新手更好地学习,例如为什么它与这个答案不同或更好 :) - Hastur
3
无论何时使用malloc(),都必须使用free()。否则代码会泄露内存,您回答中的解决方案也会泄露内存。在此类问题中分配内存却没有至少提示所需释放内存是不良实践。 - Striezel

2

现在将C++ std字符串转换为C风格字符串非常容易。

我们有string::copy函数,它可以轻松将std字符串转换为C风格字符串。参考链接

string::copy函数的参数依次为:

  1. char字符串指针
  2. 字符串大小,要复制的字符数
  3. 位置,从哪里开始复制字符

另一个重要的事情是:

此函数不会在操作结束时追加空字符。因此,我们需要手动添加。

以下是代码示例 -

// 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++ std字符串更加容易。有三种方法可以实现这种转换。
第一种方法是使用构造函数,
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;

0

假设有:

string 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)是可读但不可写的,并且指向字符串的基地址。

-1

当将std::string的底层char*缓冲区传递给期望并写入char*缓冲区的C调用时,这是特别有用的。这样你就可以兼得最好的两个世界: C++ std::string的便利性以及直接与你从C++调用的C库的可用性。

如何使用现代的C++ std::string作为一个C风格的可读/可写char*或只读的空终止符const char*

我该如何将std::string转换为char*const char*

尽管这是一个非常古老且高度投票的问题,但我即将介绍的信息还没有被充分涵盖,如果有涵盖的话也是很少,因此这是必要的补充,尤其是关于需要使用.resize()方法预先分配底层C字符串以便将其用作可写缓冲器。

以下所有用法都需要C++11或更高版本,除了char* data()调用,它需要C++17或更高版本。

要运行和测试下面的所有示例代码以及更多内容,请参见并运行我的string__use_std_string_as_a_c_str_buffer.cpp文件在我的eRCaGuy_hello_world存储库中。

快速摘要:

#include <string>
constexpr size_t BUFFER_SIZE = 100;
std::string str;
// IMPORTANT: pre-allocate the underlying buffer to guarantee what size it is
str.resize(BUFFER_SIZE); 

// -----------------------------------------------------------------------------
// Get read-writeable access to the underlying `char*` C-string at index i
// -----------------------------------------------------------------------------

char* c_str1 = &str[i]; // <=== my favorite!
char* c_str2 = str.data() + i;
char* c_str3 = &(*str.begin()) + i;

// NB: the C-strings above are NOT guaranteed to be null-terminated, so manually
// write in a null terminator at the index location where you want it if
// desired. Ex:
//
// 1. write a null terminator at some arbitrary position you choose (index 10
// here)
c_str1[10] = '\0'; 
// 2. write a null terminator at the last guaranteed valid position in the 
// underlying C-string/array of chars
c_str2[str.size() - i - 1] = '\0';

// -----------------------------------------------------------------------------
// Get read-only access to the underlying `const char*` C-string at index i
// -----------------------------------------------------------------------------
const char* const_c_str1 = &str[i];
const char* const_c_str2 = str.c_str() + i; // guaranteed to be null-terminated,
                                            // but not necessarily at the
                                            // position you desire; the
                                            // guaranteed null terminator will
                                            // be at index location 
                                            // `str.size()`

摘要:

如果您着急并需要:

  1. 底层缓冲区的读写char* C字符串:只需使用下面代码示例中的A部分技术1:char* c_str1 = &str[i];
    1. 仅需通过str.resize(BUFFER_SIZE)预先分配底层缓冲区大小(如有必要),以确保底层缓冲区足够大以满足您的需求。
  2. 底层缓冲区的只读const char* C字符串:使用与上述相同的方法(const char* const_c_str1 = &str[i]),或者const char* const_c_str1 = str.c_str() + i;
#include <string>

constexpr size_t BUFFER_SIZE = 100;

std::string str;
// IMPORTANT: pre-allocate the underlying buffer to guarantee what size it is
str.resize(BUFFER_SIZE);  

// =============================================================================
// Now you can use the `std::string`'s underlying buffer directly as a C-string
// =============================================================================

// ---------------------------------------------------------
// A. As a read-writeable `char*` C-string
// ---------------------------------------------------------

// Technique 1 [best option if using C++11]: array indexing using `operator[]` 
// to obtain a char, followed by obtaining its address with `&`
// - Documentation: 
//   https://en.cppreference.com/w/cpp/string/basic_string/operator_at
char* c_str1 = &str[0];
char* c_str2 = &str[10];
char* c_str3 = &str[33];
// etc. 

// Technique 2 [best option if using C++17]: use the `.data()` method to obtain
// a `char*` directly.
// - Documentation: 
//   https://en.cppreference.com/w/cpp/string/basic_string/data
char* c_str11 = str.data();      // same as c_str1 above
char* c_str12 = str.data() + 10; // same as c_str2 above
char* c_str13 = str.data() + 33; // same as c_str3 above

// Technique 3 [fine in C++11 or later, but is awkward, so don't do this. It is
// for demonstration and learning purposes only]: use the `.begin()` method to
// obtain an iterator to the first char, and then use the iterator's 
// `operator*()` dereference method to obtain the iterator's `char`
// `value_type`, and then take the address of that to obtain a `char*`
// - Documentation:
//   - https://en.cppreference.com/w/cpp/string/basic_string/begin
//   - https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator
char* c_str21 = &(*str.begin());      // same as c_str1 and c_str11 above
char* c_str22 = &(*str.begin()) + 10; // same as c_str2 and c_str12 above
char* c_str23 = &(*str.begin()) + 33; // same as c_str3 and c_str13 above


// ---------------------------------------------------------
// B. As a read-only, null-terminated `const char*` C-string
// ---------------------------------------------------------

// - Documentation:
//   https://en.cppreference.com/w/cpp/string/basic_string/c_str

const char* const_c_str1 = str.c_str();      // a const version of c_str1 above
const char* const_c_str2 = str.c_str() + 10; // a const version of c_str2 above
const char* const_c_str3 = str.c_str() + 33; // a const version of c_str3 above

请注意,您也可以使用.at(i).front() std::string方法,但我不会深入讨论它们,因为我认为我的示例已经足够了。有关它们的文档,请参见:
  1. https://en.cppreference.com/w/cpp/string/basic_string/at
  2. https://en.cppreference.com/w/cpp/string/basic_string/front

细节:

请参见上面的注释。我不打算涵盖使用.at(i).front()std::string方法的技术,因为我认为我已经介绍的几种技术已经足够了。

1. 使用std::string作为可读/可写的char*

要将C++ std::string用作C风格的可写char*缓冲区,您必须首先通过使用.resize()来预分配字符串的内部缓冲区以更改其.size()。请注意,仅使用.reserve()增加.capacity()是不够的! cppreference.com社区维基页面std::string::operator[]正确地说明:

如果pos > size(),则行为未定义。

resize() 方法是用来改变大小的,而不是 reserve() 方法,后者只会改变 capacity()

例如:

#include <cstring>  // `strcpy()`
#include <iostream>
#include <string>


constexpr size_t BUFFER_SIZE = 100;

std::string str;
str.resize(BUFFER_SIZE);  // pre-allocate the underlying buffer
// check the size
std::cout << "str.size() = " << str.size() << "\n";

对于下面的所有示例,请假设您有这些C字符串:

constexpr char cstr1[] = "abcde ";
constexpr char cstr2[] = "fghijk";

一旦您使用resize()预先分配了足够大的底层缓冲区,您可以以至少3种方式访问作为char*的底层缓冲区:

  1. 技巧1 [如果使用C++11,则是最佳选项]:使用operator[]进行数组索引以获取一个字符,然后使用&获取其地址。例如:
    char* c_str;
    c_str = &str[0];
    c_str = &str[5]; 
    // 等等。
    
    // 将这两个C字符串写入`std::string`的底层缓冲区
    strcpy(&str[0], cstr1);
    strcpy(&str[sizeof(cstr1) - 1], cstr2); // `- 1`以覆盖第一个空终止符
    
    // 打印字符串
    std::cout << str << "\n"; // 输出:`abcde fghijk`
    
    如果您有一个指向std::string的指针怎么办?如果您有一个指向std::string的指针,则必须先使用*pstr对其进行解引用,然后才能使用operator[]将其作为数组进行索引,如&(*pstr)[0],因此上面的语法变得有点笨拙。以下是完整示例:
    std::string str2;
    std::string* pstr = &str2;
    pstr->resize(BUFFER_SIZE);
    c_str = &(*pstr)[0]; // <=== 先解引用指针再进行索引
    // 或者,为了使优先级顺序(https://en.cppreference.com/w/cpp/language/operator_precedence)真正明显,您可以选择添加额外的括号,如下所示:
    c_str = &((*pstr)[0]);
    
  2. 技巧2 [如果使用C++17,则是最佳选项]:使用.data()方法直接获取char*例如:
    char* c_str;
    c_str = str.data();
    c_str = str.data() + 5;
    // 等等。
    
    // 将这两个C字符串写入`std::string`的底层缓冲区
    strcpy(str.data(), cstr1);
    strcpy(str.data() + (sizeof(cstr1) - 1), cstr2); // `- 1`以覆盖第一个空终止符
    
    // 打印字符串
    std::cout << str << "\n"; // 输出:`abcde fghijk`
    
  3. 技巧3 [在C++11及更高版本中可行,但很笨拙,因此不要这样做。仅供演示和学习目的]:使用.begin()方法获取指向第一个字符的迭代器,然后使用迭代器的operator*()解引用方法获取迭代器的char value_type,然后取该地址以获取char*例如:
    char* c_str;
    c_str = &(*str.begin());
    c_str = &(*str.begin()) + 5;
    // 等等。
    
    // 将这两个C字符串写入`std::string`的底层缓冲区
    strcpy(&(*str.begin()), cstr1);
    strcpy(&(*str.begin()) + (sizeof(cstr1) - 1), cstr2); // `- 1`以覆盖第一个空终止符
    
    // 打印字符串
    std::cout << str << "\n"; // 输出:`abcde fghijk`
    

需要注意的一件重要事情是,当你调用 str.resize(100) 时,它会为底层字符串至少保留 100 个字节,并将字符串的 size() 设置为 100,并将这些字符中的所有 100 个初始化为 char()--也就是char在这里查看我的问题)的默认值初始化值,即二进制零空终止符 '\0'。因此,无论字符串中是否只有"hello"后面跟着 95 个空终止符或零,每当你调用 str.size() 它都会返回 100。要获取字符串的长度非空终止符数,你必须使用 C 函数 strlen(),如下所示:

std::cout << strlen(str.c_str()) << "\n"; // prints `12` in the examples above

// instead of:
std::cout << str.size() << "\n"; // prints `100` in the examples above

2. 访问 std::string 作为只读的、以 null 结尾的 const char*

要从 std::string 获取一个可读的、以 null 结尾的 const char*,请使用 .c_str() 方法。它返回一个 C 风格的字符串,保证以 null 结尾。请注意,.data() 方法不是同一件事,因为它不能保证以 null 结尾

示例:

std::string str = "hello world";
printf("%s\n", str.c_str());

参考资料

(关于Stack Overflow上的问题)
1.如何将std::string转换为const char *或char *: 如何将std::string转换为const char *或char * 2.直接写入std::string的char*缓冲区: 直接写入std::string的char*缓冲区 3.是否有获取std:string缓冲区的方法: 是否有获取std:string缓冲区的方法 (我的内容)
1. [我的测试代码] string__use_std_string_as_a_c_str_buffer.cpp 2. [我的问题] 请参见我在此处的“相邻相关”部分: C++中`char()`、`uint8_t()`、`int64_t()`、整数`T()`等作为函数调用的含义是什么? 3. *****+ [我关于在std::string中预分配缓冲区的评论]: 直接写入std::string的char*缓冲区 4. *****+ [我关于如何预分配std::string中的存储空间,以用作char*缓冲区的评论]: 是否有获取std:string缓冲区的方法 (来自cppreference.com社区Wiki)
1. https://en.cppreference.com/w/cpp/string/basic_string

basic_string的元素是连续存储的,也就是说,对于一个basic_string s,在[0, s.size())内任意n,& *(s.begin()+n) == &*s.begin() + n,或者等价的,可以将指向s[0]的指针传递给期望指向以null结尾(自C++11起)的CharT[]数组第一个元素的函数。

2. https://en.cppreference.com/w/cpp/string/basic_string/operator_at

返回指定位置pos处的字符的引用。不进行边界检查。如果pos > size(),则行为是未定义的。

3. https://en.cppreference.com/w/cpp/string/basic_string/resize 4. https://en.cppreference.com/w/cpp/string/basic_string/reserve 5. https://en.cppreference.com/w/cpp/string/basic_string/data 6. https://en.cppreference.com/w/cpp/string/basic_string/c_str 7. https://en.cppreference.com/w/cpp/string/basic_string/clear

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接