3

Possible Duplicate:
Can I get a non-const C string back from a C++ string?

Do I need to convert it first? I saw in another post that .c_str() can be used if the function expected const char*. What about for just char*?

Community
  • 1
  • 1
thorvald
  • 217
  • 3
  • 10
  • 4
    Is there a reason why the function is asking for char* rather than const char*? – Soo Wei Tan Nov 30 '10 at 18:53
  • 2
    http://stackoverflow.com/questions/1919626/can-i-get-a-non-const-c-string-back-from-a-c-string – wkl Nov 30 '10 at 18:53
  • @SooWeiTan: No particular reason and I don't know which is better. It's a function that uses winsock. All the examples I saw used char*. – thorvald Nov 30 '10 at 19:00
  • @thorvald: So [get a good C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) and learn about `const`-correctness. That's a powerful tool you should be proficient in wielding or you might hurt yourself or others. – sbi Nov 30 '10 at 19:09
  • 1
    `foo(reinterpret_cast(&mystring))` - easy enough! What's the problem? – Edward Strange Nov 30 '10 at 19:10
  • 5
    @Noah: This is one of those cases where we would need the ability to down-vote comments into oblivion. Please remove that. It makes my toenails curl. – sbi Nov 30 '10 at 19:14
  • @Noah: Talk about spreading misinformation... – Stuart Golodetz Nov 30 '10 at 19:15
  • @Noah: Uncalled for. If I could -1 a comment... – aschepler Nov 30 '10 at 19:17
  • 1
    It may be the particular examples you are working with, but I believe the winsock library isn't that foobar'd (i.e. I think all methods take a `PCSTR`) If it is examples you are playing with, get into the habit of changing crap code like that to `const char*` or `std::string` where possible. – Nim Nov 30 '10 at 19:34
  • @Noah, what is that thing you could do in IRC, ah yes... `` ;) – Nim Nov 30 '10 at 19:38
  • Note that Visual Studio 2010 in particular does use contiguous storage for string, and supports various parts of C++0x. C++0x guarantees that `basic_string` is contiguous. Although I haven't searched MSDN for an explicit guarantee, it seems pretty unlikely that they'll sneak in a non-contiguous implementation briefly between now and when C++0x is published, or that they'll introduce one just for C++03 code. Beware though that just because the string data is contiguous, doesn't mean it's a valid input to all functions which take a non-const `char*`. `strcat`, for instance. – Steve Jessop Nov 30 '10 at 20:35
  • Ugh, most of the answers this now links to are terrible. I wish SO had a way to deal with paradigm shifts other than sticking to answers propagating the old way of doing things. – sbi Nov 30 '10 at 20:48

6 Answers6

6
std::vector<char> buffer(s.begin(), s.end());
foo(&buffer[0], buffer.size());
s.assign(buffer.begin(), buffer.end());
sbi
  • 219,715
  • 46
  • 258
  • 445
  • 1
    Although note that in practice, reputable compilers do use contiguous storage for `string`. At least according to a straw poll of the standard committee when they decided to guarantee `basic_string` contiguous in C++0x. "Reputable compilers" defined as "compilers that anyone present could think of". – Steve Jessop Nov 30 '10 at 20:29
  • @Steve: Maybe. But then, the right answer to this question should very likely have been "fix the `const` issue" anyway. – sbi Nov 30 '10 at 20:46
6

There is no way to get a char* from a string that is guaranteed to work on all platforms, for the simple fact that string is not required to use contiguous storage.

Your safest, most portable course of action is to copy the string somewhere that does use contigious storage (a vector perhaps), and use that instead.

vector<char> chars(my_string.begin(), my_string.end());
char* ptr = &chars[0];

If you want to be hacky and non-portable and decidedly unsafe, you can confirm that your string implementation does in fact use contigious storage, and then maybe use this:

&my_str[0]

But I would punch any developer that worked for me that did this.

EDIT:

I've been made aware that there are currently no known STL implementations that do not store the string data in a contiguous array, which would make &my_str[0] safe. It is also true (and I was asked to state this) that in the upcoming C++0x standard, it will be required for the storage to be contiguous.

It's been suggested that because if these facts that my post is factually incorrect.

Decide for yourself, but I say no. This is not in the current C++ standard, and so it is not required. I will still in practice do things the way I have suggested, and in any code review I will flag any code that assumes the underlying storage is contigious.

Consider this. Suppose there were a question about vtable pointers. Someone wants to examing a class and get the pointer to a virtual function by looking at the vtable. I would immediately tell them not to do this because there is no mention of how virtual methods are implemented in C++. Every implementation I know uses vtables, and I can't think of a better way to do it. It is likely that polymorphism will forever be implemented using vtables. Does that make it ok to examing the vtable directly?

IMO no, because this depends on undocumented implementation details. You have no control over this, and it could change at any time. Even if you expect it will never change, it is still bad engineering to rely on these implementation details.

Decide for yourself.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • 2
    `c_str()` must return a string with contiguous storage. So `const_cast(my_string.c_str())` *is* portable, but brings up the question of whether the `const_cast` is really safe. The `vector` way is safe if there's any doubt. – aschepler Nov 30 '10 at 19:08
  • @aschepler: But `std::string` is allowed to not to return a pointer to its internal data. (It could return a pointer to some area that's just been allocated/set aside to store the return value of `c_str()` until the next change to the string.) So even if we skip over the fact that this will invoke UB, it might not work at all. – sbi Nov 30 '10 at 19:11
  • Oh, I somehow overlooked the possibility that the function might want to modify the string contents. – aschepler Nov 30 '10 at 19:15
  • @aschepler: Why else would it take a non-`const` pointer? – sbi Nov 30 '10 at 19:23
1

When a parameter is declared as char* there it is implicitly assumed that the function will have as a side effect the modification of the string that is pointed. Based in this and the fact that c_str() does not allow modifications to the enclosed string you cannot explicitly pass an std::string to such a method.

Something like this can be achived by following the following approach:

#include <cstdlib>
#include <string>
#include <iostream>

void modify_string(char* pz)
{
    pz[0] = 'm';
}

class string_wrapper
{
    std::string& _s;
    char* _psz;
    string_wrapper(const string_wrapper&);
    string_wrapper& operator=(const string_wrapper&);
public:

    string_wrapper(std::string& s) : _s(s), _psz(0) {}

    virtual ~string_wrapper()
    {
        if(0 != _psz)
        {
            _s = _psz;
            delete[] _psz;
        }
    }

    operator char*()
    {
        _psz = new char[_s.length()+1];
        strcpy(_psz,_s.c_str());
        return _psz;
    }
};


int main(int argc, char** argv)
{
    using namespace std;
    std::string s("This is a test");
    cout << s << endl;
    modify_string(string_wrapper(s));
    cout << s << endl;
    return 0;
}
JohnP
  • 677
  • 1
  • 9
  • 18
1

There are three scenarios:


If the function is outside of your control, and it either modifies the string, or you don't and can't know if it modifies the string:

Then, copy the string into a temporary buffer, and pass that to the function, like so:

void callFoo(std::string& str);
{
    char* tmp = new char str(str.length() +1);
    strncpy(tmp, str.c_str(), str.length());
    foo(tmp);
    // Include the following line if you want the modified value:
    str = tmp;
    delete [] tmp;
}

If the function is outside of your control, but you are certain it does not modify the string, and that not taking the argument as const is simply a mistake on the API's part.

Then, you can cast the const away and pass that to the function

void callFoo(const std::string& str)
{
    foo(const_cast<char*> str.c_str());
}

You are in control of the function (and it would not be overly disruptive to change the signature).

In that case, change the function to accept either a string& (if it modifies the input buffer) or either const char* or const string& if it does not.

JohnMcG
  • 8,709
  • 6
  • 42
  • 49
0

If you are certain that the char* will not be modified, you can use const_cast to remove the const.

Soo Wei Tan
  • 3,262
  • 2
  • 34
  • 36
  • Soo Wei is correct, but please remember that your program may crash if you do this without being sure that it is okay! – leoger Nov 30 '10 at 18:58
  • 1
    Strings are not required to use contiguous storage, so this may not work. – John Dibling Nov 30 '10 at 18:58
  • `c_str()` should give you a contiguous block of memory - BUT obviously strictly read-only, which is why it's const. That function not taking a `const char *` indicates that it's either extremely badly written, or it will modify the string. – EboMike Nov 30 '10 at 19:00
  • I would create a copy of the string (or the char*) before passing it as a parameter. The state of the string object can become inconsistent very easy if doesn't know the behavior of the function – Nucc Nov 30 '10 at 19:01
  • 1
    It is guaranteed that the return value of c_str() is continuous and stays available until the next invocation of a nonconst member function, so using const_cast on that will work. – etarion Nov 30 '10 at 19:03
  • @etarion: See my reply to aschepler at [John's answer](http://stackoverflow.com/questions/4317318/how-do-i-pass-an-stdstring-to-a-function-that-expects-char/4317427#4317427). This isn't guaranteed to work. – sbi Nov 30 '10 at 19:12
  • It is, as long as you don't modify the string, which is the precondition in the answer of Soo Wei Tan. – etarion Nov 30 '10 at 19:17
  • @etarion: Ah, I overlooked that. Well, that still leaves the fact that casting away `const` on the result of `c_str()` will likely invoke UB. – sbi Nov 30 '10 at 19:22
  • Again, it won't unless you try to modify something. const_cast exists for a reason ;) casting away constness is covered in the standard, and it's defined when it's ok or not. – etarion Nov 30 '10 at 19:40
0

It's a dirty solution but I guess it works

std::string foo("example");

char* cpy = (char*)malloc(foo.size()+1);

memcpy(cpy, foo.c_str(), foo.size()+1);

Nucc
  • 1,031
  • 6
  • 20
  • There is nothing dirty about this. People are making it way more complicated than it needs to be. Fine, use new instead of malloc, but the idea remains the same. – frankc Nov 30 '10 at 19:55