22

Say I have an array of chars, which I have allocated on the heap, and which I want to convert into an std::string. Currently I am doing the following:

char *array = new char[size];
WriteIntoArray(array, size);
std::string mystring(array);
delete[] array;
return mystring; // or whatever

From what I read on the internet (http://www.cplusplus.com/reference/string/string/string/), the string constructor performs a copy of the buffer I pass it, leaving me to free the buffer (later, the string frees its internal buffer). What I would like to do is to allocate my buffer, transfer control of it to the string, and then have it free my buffer when it is destructed.

The question initializing std::string from char* without copy looked promising, but my code relies on API calls ("WriteIntoArray" in the above example) which have to write into an array of chars, so I have to create a C-style char* buffer, and cannot convert my code to use only built-in string operations (which was the answer suggested).

Is there some standard way to do this, or should I just write my own string class (ugh)? Thanks!

Community
  • 1
  • 1
fyhuang
  • 2,147
  • 5
  • 21
  • 24

3 Answers3

19

If you're using a conforming C++0x implementation, or one of the many C++ libraries that guarantee contiguous storage for std::string, then you can get a char* that points directly to the storage used by your std::string.

For example:

std::string mystring;
mystring.resize(size);
WriteIntoArray(&mystring[0], size);
return mystring;

You may have to think carefully about a null terminator, however.

John Calsbeek
  • 35,947
  • 7
  • 94
  • 101
  • 1
    No. reserve() reserves the space, but it does not belong to the string (yet). Accessing a[0] or a[1] is still UB. If you checkout size() it will still be zero. What you actual want is resize() – Martin York Jul 14 '11 at 22:13
  • 2
    You can't use reserve(), you must use resize(). And then figure out a proper resize() after the call. – Bo Persson Jul 14 '11 at 22:13
  • 2
    `reserve` needs to be `resize`. What does "think carefully about a null terminator" really mean? Are any guarantees made as to whether the array pointed to by `&mystring[0]` must be null-terminated? Is `std::string` responsible for ensuring that such a terminator is there? If `WriteIntoArray` writes a null terminator, should it be removed? (I ask because I don't know what is guaranteed and required by `std::string`.) – James McNellis Jul 14 '11 at 22:14
  • Before C++0x, `std::string` is only responsible for producing a null terminator when invoking `c_str()`. I would imagine that it would "just work" without manually inserting a null terminator, but I'm not 100% positive. – John Calsbeek Jul 14 '11 at 22:17
  • 2
    Presumably `WriteIntoArray` puts on a null terminator, which would need to be removed after the call; you probably want to add `mystring.resize(size-1);` just before returning. If it doesn't add the null, then you should be using `vector` instead of `string`. – Mark Ransom Jul 14 '11 at 22:22
  • We don't know where the null terminator will be. The original code copied a C string of any length. – Bo Persson Jul 14 '11 at 22:25
  • Will this code works when size=10 and array="ab\0trash.." mystring.size() will be 10 instead of 2? – RiaD Jul 14 '11 at 22:25
  • @RiaD: It will make the string length 10, yes; `std::string` can contain embedded NULs. So if `size` is a maximum, not an exact length, this could wouldn't be optimal. – John Calsbeek Jul 14 '11 at 22:26
  • My answer feels lonely even though it was first and the same as this :-( – Martin York Jul 14 '11 at 22:29
  • Thanks for the responses! This feels nitpicky, but is it necessary for STL strings to be contiguous in memory? I thought I read somewhere that they didn't have to be, so writing into `&mystring[0]`, `mystring.data()`, etc. could technically not work? Someone correct me on this? – fyhuang Jul 14 '11 at 22:50
  • `&mystring[0]` is guaranteed to return a pointer to contiguous memory, but up until C++0x it wasn't guaranteed that `std::string` uses contiguous storage internally. If that seems like a pointless distinction, that's because it pretty much is. Virtually every standard library uses contiguous storage. – John Calsbeek Jul 14 '11 at 23:22
  • Okay, I think I will just go with that. I am using the STL that comes with MSVC so I think it should be okay. – fyhuang Jul 15 '11 at 00:26
  • Does the standard actually permit modifying the string buffer like this? Considering that `data()` returns a `const char *` (even in C++11), it seems to be a hack... even if the hack will probably work for all actual implementations. – Søren Løvborg Oct 29 '14 at 19:52
  • 3
    @SørenLøvborg Yes, it is permitted in C++11. There's a lot of arcane wording around this, which pretty much boils down to it being illegal to modify the null terminator, and being illegal to modify anything through the data() or c_str() pointers, but valid through &str[0]. http://stackoverflow.com/a/14291203/5696 – John Calsbeek Oct 30 '14 at 02:31
12

You may implement your own std::allocator that instead of allocating new memory for your string, uses as the source the region you already have instantiated.

You should then instantiate your std::string using your allocator.

http://www.codeproject.com/Articles/4795/C-Standard-Allocator-An-Introduction-and-Implement

Arturo
  • 121
  • 1
  • 2
6

You can't std::string owns the pointer and thus copies the data into a space that it allocates.

What you could do is:

std::string  mystring(size, ' ');
WriteIntoArray(&mystring[0], size);
return mystring; // or whatever
Martin York
  • 257,169
  • 86
  • 333
  • 562