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


当前回答

我正在使用一个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不可复制)。

其他回答

如果你只是想将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::string是const, .data()将返回const char *,就像.c_str()一样。

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

指针指向一个以空结束的字符串,终止符不计入str.size()。不允许将非空字符分配给结束符。

这在将std::string的底层char*缓冲区传递给C调用时特别有用,这些调用期望并写入char*缓冲区。这样你可以两全其美!: 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或更高版本。

要运行和测试下面所有示例代码,请查看并运行eRCaGuy_hello_world repo中的string__use_std_string_as_c_str_buffer .cpp文件。

快速总结:

#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()`

简介:

如果有急事,你需要:

底层缓冲区的可读写char* C-string:只需使用下面代码示例中的技术1部分:char* c_str1 = &str[i];。 只要确保首先通过str.resize(BUFFER_SIZE)预先分配底层缓冲区大小(如果需要的话),就可以确保底层缓冲区足够大以满足您的需要。 底层缓冲区的只读const char* C-string:使用与上面相同的方法(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方法,但我不会深入讨论这些方法,因为我认为我的例子已经足够了。有关它们的文档,请参见:

https://en.cppreference.com/w/cpp/string/basic_string/at 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()是不够的!std::string::operator[]的cppreference.com社区wiki页面正确地声明:

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

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

Ex:

#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()预先分配了一个足够大的底层缓冲区,你就可以访问底层缓冲区 char*至少在3个方面:

Technique 1 [best option if using C++11]: array indexing using operator[] to obtain a char, followed by obtaining its address with &. Ex: char* c_str; c_str = &str[0]; c_str = &str[5]; // etc. // Write these 2 C-strings into a `std::string`'s underlying buffer strcpy(&str[0], cstr1); strcpy(&str[sizeof(cstr1) - 1], cstr2); // `- 1` to overwrite the first // null terminator // print the string std::cout << str << "\n"; // output: `abcde fghijk` What if you have a pointer to a std::string? If you have a ptr to a std::string, it must be dereferenced first with *pstr before you can index into it as an array with the operator[] as &(*pstr)[0], so the syntax above becomes a little more awkward. Here is a full example: std::string str2; std::string* pstr = &str2; pstr->resize(BUFFER_SIZE); c_str = &(*pstr)[0]; // <=== dereference the ptr 1st before indexing into it // Or, to make the order of precedence // (https://en.cppreference.com/w/cpp/language/operator_precedence) really // obvious, you can optionally add extra parenthesis like this: c_str = &((*pstr)[0]); Technique 2 [best option if using C++17]: use the .data() method to obtain a char* directly. Ex: char* c_str; c_str = str.data(); c_str = str.data() + 5; // etc. // Write these 2 C-strings into the `std::string`'s underlying buffer strcpy(str.data(), cstr1); strcpy(str.data() + (sizeof(cstr1) - 1), cstr2); // `- 1` to overwrite the // first null terminator // print the string std::cout << str << "\n"; // output: `abcde fghijk` Technique 3 [fine in C++11 and 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*. Ex: char* c_str; c_str = &(*str.begin()); c_str = &(*str.begin()) + 5; // etc. // Write these 2 C-strings into the `std::string`'s underlying buffer strcpy(&(*str.begin()), cstr1); strcpy(&(*str.begin()) + (sizeof(cstr1) - 1), cstr2); // `- 1` to overwrite // the first null // terminator // print the string std::cout << str << "\n"; // output: `abcde fghijk`

Something important to be aware of is that when you call str.resize(100), it reserves at least 100 bytes for the underlying string, sets the size() of the string to 100, and initializes all 100 of those chars to char()--AKA: the default value initialization value for char (see my question here), which is the binary zero null-terminator, '\0'. Therefore, whenever you call str.size() it will return 100 even if the string simply has "hello" in it followed by 95 null-terminators, or zeros. To get the length, or number of non-null-terminators in the string, you'll have to resort to the C function strlen(), like this:

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风格的字符串,保证以空结束。注意.data()方法不是一回事,因为它不能保证以空结束!

例子:

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

参考文献

(questions on Stack Overflow) How to convert a std::string to const char* or char*: How to convert a std::string to const char* or char* Directly write into char* buffer of std::string: Directly write into char* buffer of std::string Is there a way to get std:string's buffer: Is there a way to get std:string's buffer (my content) [my test code] string__use_std_string_as_a_c_str_buffer.cpp [my Q] See the "Adjacently related" section at the bottom of my question here: What is a call to `char()`, `uint8_t()`, `int64_t()`, integer `T()`, etc, as a function in C++? *****+ [my comments about pre-allocating a buffer in the std::string]: Directly write into char* buffer of std::string *****+ [my comment on how to pre-allocate storage in a std::string, to be used as a char* buffer] Is there a way to get std:string's buffer (from the cppreference.com community wiki) https://en.cppreference.com/w/cpp/string/basic_string: The elements of a basic_string are stored contiguously, that is, for a basic_string s, &*(s.begin () + n) == &*s.begin() + n for any n in [0, s.size()), or, equivalently, a pointer to s[0] can be passed to functions that expect a pointer to the first element of a null-terminated (since C++11)CharT[] array. https://en.cppreference.com/w/cpp/string/basic_string/operator_at Returns a reference to the character at specified location pos. No bounds checking is performed. If pos > size(), the behavior is undefined. https://en.cppreference.com/w/cpp/string/basic_string/resize https://en.cppreference.com/w/cpp/string/basic_string/reserve https://en.cppreference.com/w/cpp/string/basic_string/data https://en.cppreference.com/w/cpp/string/basic_string/c_str https://en.cppreference.com/w/cpp/string/basic_string/clear

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

你可以使用&mystring[0]来获得一个char *指针,但是有几个问题:你不一定会得到一个以0结尾的字符串,你也不能改变字符串的大小。特别要注意的是,不要添加超过字符串结尾的字符,否则会导致缓冲区溢出(并可能导致崩溃)。

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

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

我正在使用一个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不可复制)。

从c++ std字符串转换到c风格字符串现在真的很容易。

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

字符串::连续复制函数形参

Char字符串指针 字符串大小,b复制多少个字符 位置,从字符复制开始的位置

另一件重要的事,

此函数不会在操作结束时附加空字符。所以,我们需要手动放置它。

代码考试在下面

// 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字符串要容易得多

有三种方法可以将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;