1

I have been working with C++ strings and trying to load char * strings into std::string by using C functions such as strcpy(). Since strcpy() takes char * as a parameter, I have to cast it which goes something like this:

std::string destination;
unsigned char *source;
strcpy((char*)destination.c_str(), (char*)source);

The code works fine and when I run the program in a debugger, the value of *source is stored in destination, but for some odd reason it won't print out with the statement

std::cout << destination;

I noticed that if I use

std::cout << destination.c_str();

The value prints out correctly and all is well. Why does this happen? Is there a better method of copying an unsigned char* or char* into a std::string (stringstreams?) This seems to only happen when I specify the string as foo.c_str() in a copying operation.

Edit: To answer the question "why would you do this?", I am using strcpy() as a plain example. There are other times that it's more complex than assignment. For example, having to copy only X amount of string A into string B using strncpy() or passing a std::string to a function from a C library that takes a char * as a parameter for a buffer.

user99545
  • 1,173
  • 3
  • 16
  • 32

7 Answers7

9

Here's what you want

 std::string destination = source;

What you're doing is wrong on so many levels... you're writing over the inner representation of a std::string... I mean... not cool man... it's much more complex than that, arrays being resized, read-only memory... the works.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • Found one for you. Though a bit different, it does relate: http://stackoverflow.com/questions/6456359/what-is-stdstringc-str-lifetime – chris May 08 '12 at 22:16
4

This is not a good idea at all for two reasons:

  1. destination.c_str() is a const pointer and casting away it's const and writing to it is undefined behavior.
  2. You haven't set the size of the string, meaning that it won't even necessealy have a large enough buffer to hold the string which is likely to cause an access violation.

std::string has a constructor which allows it to be constructed from a char* so simply write:

std::string destination = source

Benj
  • 31,668
  • 17
  • 78
  • 127
3

Well what you are doing is undefined behavior. Your c_str() returns a const char * and is not meant to be assigned to. Why not use the defined constructor or assignment operator.

rerun
  • 25,014
  • 6
  • 48
  • 78
3

std::string defines an implicit conversion from const char* to std::string... so use that.

You decided to cast away an error as c_str() returns a const char*, i.e., it does not allow for writing to its underlying buffer. You did everything you could to get around that and it didn't work (you shouldn't be surprised at this).

c_str() returns a const char* for good reason. You have no idea if this pointer points to the string's underlying buffer. You have no idea if this pointer points to a memory block large enough to hold your new string. The library is using its interface to tell you exactly how the return value of c_str() should be used and you're ignoring that completely.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
1

Do not do what you are doing!!!

I repeat!

DO NOT DO WHAT YOU ARE DOING!!!

That it seems to sort of work when you do some weird things is a consequence of how the string class was implemented. You are almost certainly writing in memory you shouldn't be and a bunch of other bogus stuff.

When you need to interact with a C function that writes to a buffer there's two basic methods:

std::string read_from_sock(int sock) {
    char buffer[1024] = "";

    int recv = read(sock, buffer, 1024);
    if (recv > 0) {
      return std::string(buffer, buffer + recv);
    }
    return std::string();
}

Or you might try the peek method:

std::string read_from_sock(int sock) {

    int recv = read(sock, 0, 0, MSG_PEEK);
    if (recv > 0) {
      std::vector<char> buf(recv);
      recv = read(sock, &buf[0], recv, 0);
      return std::string(buf.begin(), buf.end());
    }
    return std::string();
}

Of course, these are not very robust versions...but they illustrate the point.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
0

First you should note that the value returned by c_str is a const char* and must not be modified. Actually it even does not have to point to the internal buffer of string.

Adam Badura
  • 5,069
  • 1
  • 35
  • 70
0

In response to your edit:

having to copy only X amount of string A into string B using strncpy()

If string A is a char array, and string B is std::string, and strlen(A) >= X, then you can do this:

B.assign(A, A + X);

passing a std::string to a function from a C library that takes a char * as a parameter for a buffer

If the parameter is actually const char *, you can use c_str() for that. But if it is just plain char *, and you are using a C++11 compliant compiler, then you can do the following:

c_function(&B[0]);

However, you need to ensure that there is room in the string for the data(same as if you were using a plain c-string), which you can do with a call to the resize() function. If the function writes an unspecified amount of characters to the string as a null-terminated c-string, then you will probably want to truncate the string afterward, like this:

B.resize(B.find('\0'));

The reason you can safely do this in a C++11 compiler and not a C++03 compiler is that in C++03, strings were not guaranteed by the standard to be contiguous, but in C++11, they are. If you want the guarantee in C++03, then you can use std::vector<char> instead.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • Regarding your advice to pass the return value of `c_str()` to a function which takes a `char*`... that's a bad idea. You have no idea what that `const char*` is pointing to. I see nowhere where it is stated that it points to the internal buffer used by the string, so it may not even work, and the API is yelling at you that this memory should not be modified. Unless you know for a fact that the function will not modify what the pointer points to, don't do it. – Ed S. May 08 '12 at 23:37
  • @EdS. -- Good thing I didn't say that. – Benjamin Lindley May 08 '12 at 23:39