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

How can I convert an std::string to a char* or a const char*?

转载于:https://stackoverflow.com/questions/347949/how-to-convert-a-stdstring-to-const-char-or-char

csdnceshi53
Lotus@ No, but char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest) would be more idiomatic C++. strcpy() and malloc() aren't wrong or problematic, but it seems inconsistent to use a C++ string and C library facilities with C++ equivalents in the same block of code.
接近 6 年之前 回复
weixin_41568134
MAO-EYE you mean they are imaginary?
接近 6 年之前 回复
csdnceshi53
Lotus@ strcpy and malloc aren't really the C++ way.
接近 6 年之前 回复
weixin_41568134
MAO-EYE char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
大约 6 年之前 回复
weixin_41568196
撒拉嘿哟木头 You can't use str.size() unless the size is known at compile time, also it might overflow your stack if the fixed size value is huge.
接近 8 年之前 回复
csdnceshi56
lrony* Instead of: char * writable = new char[str.size() + 1]; You can use char writable[str.size() + 1]; Then you don't need to worry about deleting writable or exception handling.
大约 10 年之前 回复

8个回答

If you just want to pass a std::string to a function that needs const char* you can use

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

If you want to get a writable copy, like char *, you can do that with this:

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;

Edit: Notice that the above is not exception safe. If anything between the new call and the delete call throws, you will leak memory, as nothing will call delete for you automatically. There are two immediate ways to solve this.

boost::scoped_array

boost::scoped_array will delete the memory for you upon going out of scope:

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

This is the standard way (does not require any external library). You use std::vector, which completely manages the memory for you.

std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');

// get the char* using &writable[0] or &*writable.begin()
csdnceshi61
derek5. please fix it. I community wikied it
大约 2 年之前 回复
csdnceshi54
hurriedly% Given that the issues with simply using data or &s[0] have been fixed for three released language versions now, this all seems overly complicated. Even if the string is const, a simple copy of it should be enough.
大约 2 年之前 回复
csdnceshi57
perhaps? As of C++17, std::string::data() now returns a CharT* instead of a const CharT*. It might be a good idea to update this answer :)
3 年多之前 回复
weixin_41568183
零零乙 Instead of boost::scoped_array<char>, one can also use std::unique_ptr<char[]> from the STL: std::unique_ptr<char[]> writable(new char[str.size() + 1]);. It also deallocates the array using delete[] when it goes out of scope.
大约 4 年之前 回复
csdnceshi52
妄徒之命 Solutions involving strcpy() or strdup() can fail if the std::string contains embedded null characters.
4 年多之前 回复
csdnceshi67
bug^君 For the first code block, is it deep or shallow copy that is performed?
5 年多之前 回复
csdnceshi77
狐狸.fox That sounds incredibly wasteful, but I'm sure there's some reason for it. I very rarely use the C++ standard library and stick to the C stdlib, so I haven't had much practice with these. Would have expected that .end() would return a pointer-size int, or something similar.
接近 6 年之前 回复
csdnceshi61
derek5. what I worried more about is the end() + 1, not so much about the null terminator (I am no library expert, but I don't think that any of the popular library implementations around today have std::string without a trailing \0). In debug builds, some library implementations have containers where .end() returns a class object that throws/core-dumps on .end() + 1. So I better be cautious and write code that I definitely know works :)
接近 6 年之前 回复
csdnceshi77
狐狸.fox Ah, so in 03 it was not guaranteed to have a null terminator? That makes sense.
接近 6 年之前 回复
csdnceshi61
derek5. because in C++03 that was not valid. I am not sure what the exact rule in C++11 and C++14 is for it. They guarantee that the string is zero terminated and contiguous, at least
接近 6 年之前 回复
csdnceshi77
狐狸.fox Instead of std::copy(str.begin(), str.end(), writable); writable[str.size()] = '\0';, why not just use std::copy(str.begin(),str.end() + 1,writable);?
接近 6 年之前 回复
csdnceshi72
谁还没个明天 char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
大约 6 年之前 回复
csdnceshi76
斗士狗 Why not copy the string and pass &copy[0] for a writable char* of a fixed size?
大约 6 年之前 回复
csdnceshi61
derek5. that's totally fine and perhaps I would have used it in real code aswell when in hurry :) I just like to use std::copy when I have time to make nice code. Let's not forget there's a difference (albeit an unlikely one with real strings - but who knows what he's going to store). The std::copy will copy embedded null bytes aswell, till the end of the std::string.
大约 6 年之前 回复
csdnceshi59
ℙℕℤℝ As a C++ neophyte, why wouldn't I just use std::strcpy(dest, str.c_str())?
大约 6 年之前 回复
csdnceshi51
旧行李 You should add a comment "DON'T DO THIS" next to the new char[..] statement. I suspect that many people just copy/paste the code sample without reading the accompanying text.
6 年多之前 回复
csdnceshi78
程序go I was young...forgive me. :(
6 年多之前 回复
csdnceshi78
程序go Is char *p = const_cast<char*>(str.c_str()) an option (albeit a bad one)?
7 年多之前 回复
weixin_41568126
乱世@小熊 I'd suggest it is equally the "C++ way" to simply mutate std::string in-place as its public interface exposes char &operator[]: this is specified and guaranteed behaviour.
7 年多之前 回复
csdnceshi69
YaoRaoLov BTW, if you use something like strdup(), you must call free() later (as opposed to delete []).
大约 8 年之前 回复
csdnceshi62
csdnceshi62 Baker: That's an imaginary ctor I've come up with :) I've mixed it up with std::string's ctor (or maybe it was a non-standard extension of VC6... shudder). Anyway, my preferred solution is the one mentioned in litb's comment above (with begin/end iters).
9 年多之前 回复
csdnceshi53
Lotus@ Which vector constructor is vector<char> writable(str.c_str(), str.size() + 1); using?
9 年多之前 回复
csdnceshi79
python小菜 +1 (I was waiting for the exception safe addition)... ^_^
11 年多之前 回复
csdnceshi61
derek5. std::copy is the c++ way of doing this, without the need of getting at the string pointer. I try to avoid using C functions as much as i can.
11 年多之前 回复
weixin_41568131
10.24 I'm curious why you're bending over backwards to use std::copy(). Wouldn't using strcpy( the_destination, str.c_str()) - or strlcpy() if you have it - work as well and be more immediately obvious what's going on? strcpy() will work with any of scoped_array, vector, or a raw memory allocation.
11 年多之前 回复
csdnceshi61
derek5. ok, thanks i will put them there. you've convinced me
11 年多之前 回复
csdnceshi79
python小菜 I agree: litb, you should add your "vector" solution to your main answer, as it is (exception-) safer than the raw new/delete solution.
11 年多之前 回复
csdnceshi50
三生石@ Definitely use std::vector. Allocating the array of char is not exception safe.
11 年多之前 回复
csdnceshi62
csdnceshi62 You could also construct the vector with: vector<char> writable(str.c_str(), str.size() + 1);
11 年多之前 回复
csdnceshi61
derek5. what i would probably prefer generally is std::vector<char> writable(str.begin(), str.end()); writable.push_back('\0'); char * c = &writable[0];
11 年多之前 回复
csdnceshi61
derek5. you could, but strdup is not a c or c++ standard function, it's from posix :)
11 年多之前 回复
weixin_41568110
七度&光 Simply use char *result = strdup(str.c_str());
11 年多之前 回复

Given say...

std::string x = "hello";

Getting a `char *` or `const char*` from a `string`

How to get a character pointer that's valid while x remains in scope and isn't modified further

C++11 simplifies things; the following all give access to the same internal string buffer:

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...

All the above pointers will hold the same value - the address of the first character in the buffer. Even an empty string has a "first character in the buffer", because C++11 guarantees to always keep an extra NUL/0 terminator character after the explicitly assigned string content (e.g. std::string("this\0that", 9) will have a buffer holding "this\0that\0").

Given any of the above pointers:

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

Only for the non-const pointer from &x[0]:

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

Writing a NUL elsewhere in the string does not change the string's size(); string's are allowed to contain any number of NULs - they are given no special treatment by std::string (same in C++03).

In C++03, things were considerably more complicated (key differences highlighted):

  • x.data()

    • returns const char* to the string's internal buffer which wasn't required by the Standard to conclude with a NUL (i.e. might be ['h', 'e', 'l', 'l', 'o'] followed by uninitialised or garbage values, with accidental accesses thereto having undefined behaviour).
      • x.size() characters are safe to read, i.e. x[0] through x[x.size() - 1]
      • for empty strings, you're guaranteed some non-NULL pointer to which 0 can be safely added (hurray!), but you shouldn't dereference that pointer.
  • &x[0]

    • for empty strings this has undefined behaviour (21.3.4)
      • e.g. given f(const char* p, size_t n) { if (n == 0) return; ...whatever... } you mustn't call f(&x[0], x.size()); when x.empty() - just use f(x.data(), ...).
    • otherwise, as per x.data() but:
      • for non-const x this yields a non-const char* pointer; you can overwrite string content
  • x.c_str()

    • returns const char* to an ASCIIZ (NUL-terminated) representation of the value (i.e. ['h', 'e', 'l', 'l', 'o', '\0']).
    • although few if any implementations chose to do so, the C++03 Standard was worded to allow the string implementation the freedom to create a distinct NUL-terminated buffer on the fly, from the potentially non-NUL terminated buffer "exposed" by x.data() and &x[0]
    • x.size() + 1 characters are safe to read.
    • guaranteed safe even for empty strings (['\0']).

Consequences of accessing outside legal indices

Whichever way you get a pointer, you must not access memory further along from the pointer than the characters guaranteed present in the descriptions above. Attempts to do so have undefined behaviour, with a very real chance of application crashes and garbage results even for reads, and additionally wholesale data, stack corruption and/or security vulnerabilities for writes.

When do those pointers get invalidated?

If you call some string member function that modifies the string or reserves further capacity, any pointer values returned beforehand by any of the above methods are invalidated. You can use those methods again to get another pointer. (The rules are the same as for iterators into strings).

See also How to get a character pointer valid even after x leaves scope or is modified further below....

So, which is better to use?

From C++11, use .c_str() for ASCIIZ data, and .data() for "binary" data (explained further below).

In C++03, use .c_str() unless certain that .data() is adequate, and prefer .data() over &x[0] as it's safe for empty strings....

...try to understand the program enough to use data() when appropriate, or you'll probably make other mistakes...

The ASCII NUL '\0' character guaranteed by .c_str() is used by many functions as a sentinel value denoting the end of relevant and safe-to-access data. This applies to both C++-only functions like say fstream::fstream(const char* filename, ...) and shared-with-C functions like strchr(), and printf().

Given C++03's .c_str()'s guarantees about the returned buffer are a super-set of .data()'s, you can always safely use .c_str(), but people sometimes don't because:

  • using .data() communicates to other programmers reading the source code that the data is not ASCIIZ (rather, you're using the string to store a block of data (which sometimes isn't even really textual)), or that you're passing it to another function that treats it as a block of "binary" data. This can be a crucial insight in ensuring that other programmers' code changes continue to handle the data properly.
  • C++03 only: there's a slight chance that your string implementation will need to do some extra memory allocation and/or data copying in order to prepare the NUL terminated buffer

As a further hint, if a function's parameters require the (const) char* but don't insist on getting x.size(), the function probably needs an ASCIIZ input, so .c_str() is a good choice (the function needs to know where the text terminates somehow, so if it's not a separate parameter it can only be a convention like a length-prefix or sentinel or some fixed expected length).

How to get a character pointer valid even after x leaves scope or is modified further

You'll need to copy the contents of the string x to a new memory area outside x. This external buffer could be in many places such as another string or character array variable, it may or may not have a different lifetime than x due to being in a different scope (e.g. namespace, global, static, heap, shared memory, memory mapped file).

To copy the text from std::string x into an independent character array:

// 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);

Other reasons to want a char* or const char* generated from a string

So, above you've seen how to get a (const) char*, and how to make a copy of the text independent of the original string, but what can you do with it? A random smattering of examples...

  • give "C" code access to the C++ string's text, as in printf("x is '%s'", x.c_str());
  • copy x's text to a buffer specified by your function's caller (e.g. strncpy(callers_buffer, callers_buffer_size, x.c_str())), or volatile memory used for device I/O (e.g. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;)
  • append x's text to an character array already containing some ASCIIZ text (e.g. strcat(other_buffer, x.c_str())) - be careful not to overrun the buffer (in many situations you may need to use strncat)
  • return a const char* or char* from a function (perhaps for historical reasons - client's using your existing API - or for C compatibility you don't want to return a std::string, but do want to copy your string's data somewhere for the caller)
    • be careful not to return a pointer that may be dereferenced by the caller after a local string variable to which that pointer pointed has left scope
    • some projects with shared objects compiled/linked for different std::string implementations (e.g. STLport and compiler-native) may pass data as ASCIIZ to avoid conflicts
csdnceshi67
bug^君 Nice one. Another reason to want a char* (non const) is to operate with MPI broadcast. It looks nicer if you don't have to copy back and forth. I would have personally offered a char* const getter to string. Const pointer, but editable string. Although it may have messed with the implicit conversion from const char* to string...
5 年多之前 回复

Use the .c_str() method for const char *.

You can use &mystring[0] to get a char * pointer, but there are a couple of gotcha's: you won't necessarily get a zero terminated string, and you won't be able to change the string's size. You especially have to be careful not to add characters past the end of the string or you'll get a buffer overrun (and probable crash).

There was no guarantee that all of the characters would be part of the same contiguous buffer until C++11, but in practice all known implementations of std::string worked that way anyway; see Does “&s[0]” point to contiguous characters in a std::string?.

Note that many string member functions will reallocate the internal buffer and invalidate any pointers you might have saved. Best to use them immediately and then discard.

csdnceshi61
derek5. char* result = strcpy(malloc(str.length()+1), str.c_str());
大约 6 年之前 回复
csdnceshi63
elliott.david thanks - I didn't know that. I'd be hard pressed to find an implementation where that wasn't the case, though.
11 年多之前 回复
csdnceshi79
python小菜 Technically, std::string storage will be contiguous only in C++0x.
11 年多之前 回复
csdnceshi63
elliott.david Argh! That's what I get for trying to whip up a quick answer. I've used your solution in the past, don't know why it wasn't the first thing that came to mind. I've edited my answer.
11 年多之前 回复
weixin_41568184
叼花硬汉 you should note that data() returns const char * :) what you mean is &str[0], which returns a contiguous, but not necassary null terminated string.
11 年多之前 回复

I am working with an API with a lot of functions get as an input a char*.

I have created a small class to face this kind of problem, I have implemented the RAII idiom.

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

And you can use it as:

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

I have called the class DeepString because it is creating a deep and unique copy (the DeepString is not copyable) of an existing string.

weixin_41568184
叼花硬汉 I would avoid this naming convention. c_str() as used by std is an abbreviation for "C-string" not "const string" and str() always returns a std::basic_string, not char* (for example std::stringstream::str())
接近 5 年之前 回复
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
csdnceshi79
python小菜 Whenever there is a malloc(), there also has to be a free(). Otherwise the code leaks memory, and so does the solution in your answer. Allocating memory without at least hinting to the required deallocation is bad practice for such questions.
接近 4 年之前 回复
csdnceshi75
衫裤跑路 Yes the functions are basic but... did you remember when you start to deal with a programming language? Some lines more to explain and it will really help a neophyte to learn why for example is different or better than this answer:)
接近 5 年之前 回复
weixin_41568208
北城已荒凉 yes the functions are basic but you've twisted and bent them to look like bowl of spaghetti or one liner Frankenstein's monster :)
5 年多之前 回复
weixin_41568131
10.24 strcpy(), malloc(), length() and c_str() are basic functions and there is nothing hard in this. Just allocating memory and copying.
5 年多之前 回复
weixin_41568208
北城已荒凉 looks fancy but really hard to understand... Simple is the best IMO
5 年多之前 回复

C++17

C++17 (upcoming standard) changes the synopsis of the template basic_string adding a non const overload of data():

charT* data() noexcept;

Returns: A pointer p such that p + i == &operator for each i in [0,size()].


CharT const * from std::basic_string<CharT>

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

CharT * from std::basic_string<CharT>

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

C++11

CharT const * from std::basic_string<CharT>

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

CharT * from std::basic_string<CharT>

From C++11 onwards, the standard says:

  1. The char-like objects in a basic_string object shall be stored contiguously. That is, for any basic_string object s, the identity &*(s.begin() + n) == &*s.begin() + n shall hold for all values of n such that 0 <= n < s.size().

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

    Returns: *(begin() + pos) if pos < size(), otherwise a reference to an object of type CharT with value CharT(); the referenced value shall not be modified.


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

    Returns: A pointer p such that p + i == &operator[](i) for each i in [0,size()].

There are severable possible ways to get a non const character pointer.

1. Use the contiguous storage of C++11

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

Pro

  • Simple and short
  • Fast (only method with no copy involved)

Cons

  • Final '\0' is not to be altered / not necessarily part of the non-const memory.

2. Use std::vector<CharT>

std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();

Pro

  • Simple
  • Automatic memory handling
  • Dynamic

Cons

  • Requires string copy

3. Use std::array<CharT, N> if N is compile time constant (and small enough)

std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());

Pro

  • Simple
  • Stack memory handling

Cons

  • Static
  • Requires string copy

4. Raw memory allocation with automatic storage deletion

std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);

Pro

  • Small memory footprint
  • Automatic deletion
  • Simple

Cons

  • Requires string copy
  • Static (dynamic usage requires lots more code)
  • Less features than vector or array

5. Raw memory allocation with manual handling

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

Pro

  • Maximum 'control'

Con

  • Requires string copy
  • Maximum liability / susceptibility for errors
  • Complex

Just see this :

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

However , note that this will return a const char *.For a char *, use strcpy to copy it into another char array.

csdnceshi54
hurriedly% Personally, I appreciate the simplicity.
大约 6 年之前 回复
weixin_41568184
叼花硬汉 Hi, what you posted has already been said multiple times, with more details, in other answers to the 5 year old question. It's fine to answer older questions, but only if you add new information. Otherwise, it's just noise.
大约 7 年之前 回复

Try this

std::string s(reinterpret_cast<const char *>(Data), Size);
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问