1

I have done a search in google and been told this is impossible as I can only get a static char * from a string, so I am looking for an alternative.

Here is the situation: I have a .txt file that contains a list of other .txt files and some numbers, this is done so the program can be added to without recompilation. I use an ifstream to read the filenames into a string.

The function that they are required for is expecting a char * not a string and apparently this conversion is impossible.

I have access to this function but it calls another function with the char * so I think im stuck using a char *.

Does anyone know of a work around or another way of doing this?

quamrana
  • 37,849
  • 12
  • 53
  • 71
Skeith
  • 2,512
  • 5
  • 35
  • 57
  • 1
    The obvious question is: Can you not modify your existing function(s) to take a `const char *`? If those functions don't modify the input, then they really should be `const`. – Oliver Charlesworth Apr 11 '11 at 11:02
  • @Oli It does modify the string im afraid hence the trouble – Skeith Apr 11 '11 at 11:14
  • Do you want the changes to be visible back in the original `std::string`? – Oliver Charlesworth Apr 11 '11 at 11:17
  • @Oli the program reads this one file. in it are the names of all the files it has to load in order to run the game. maps can be added or subtracted at the designers whim. As long as the function gets them I never use them again, if I could I would read them in as char *. – Skeith Apr 11 '11 at 11:25

5 Answers5

12

In C++, I’d always do the following if a non-const char* is needed:

std::vector<char> buffer(str.length() + 1, '\0');
std::copy(str.begin(), str.end(), buffer.begin());
char* cstr = &buffer[0];

The first line creates a modifiable copy of our string that is guaranteed to reside in a contiguous memory block. The second line gets a pointer to the beginning of this buffer. Notice that the vector is one element bigger than the string to accomodate a null termination.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    Oh, hang on. Will this give you a null terminator? – Oliver Charlesworth Apr 11 '11 at 11:10
  • @Konrad: +1 reinstated! The major advantage of this over @codebolt's solution is that there's no need to remember to `delete`. – Oliver Charlesworth Apr 11 '11 at 11:14
  • 1
    @Konrad: I prefer `strcpy` when it's appropriate, as here. It's pointless to use complex C++ methods when simpler ones are available. To prove my point, your code still has a bug (actually two, but one of them is a typo). – TonyK Apr 11 '11 at 11:19
  • Or const char* tmp = str.c_str(); std::vector buffer(tmp, tmp+str.size()+1); char* cstr = &buffer[0]; (possible extra copy (c_str()), but buffer is only filled once) – stefaanv Apr 11 '11 at 11:21
  • @Tony: What's the non-typo bug? – Oliver Charlesworth Apr 11 '11 at 11:25
  • @Oli: `vector` instead of `vector`. Didn't anybody try to compile this? – TonyK Apr 11 '11 at 11:38
  • @Tony: Ok, but that's really just another typo, which the compiler should spot for you. – Oliver Charlesworth Apr 11 '11 at 11:39
  • You can make this much simpler (and remove the copy), `vector buffer(str.begin(), str.end()); buffer.push_back('\0'); // use` – Nim Apr 11 '11 at 11:55
  • 1
    @Nim But that’s unfortunately potentially much more inefficient. – Konrad Rudolph Apr 11 '11 at 14:39
  • 2
    @TonyK I don’t agree that `strcpy` is simpler – at all. The mistakes I made in the code are almost all caught by the compiler. The `strcpy` code has no such safety harness. It just quietly leaks memory or overwrites buffers. Code which has this potential is just *never* appropriate. There is nothing “simpler” about the `new char`/`strcpy` method, for any meaningful usage of the word “simpler”. – Konrad Rudolph Apr 11 '11 at 14:42
  • @Konrad - okay so there is a potential resize that could happen as part of the `push_back()`, but is there anything else? If you wanted to avoid that, I guess you could use the member function `assign` (which then would be almost the same as `std::copy` I guess) – Nim Apr 11 '11 at 15:05
  • @Nim The resize is the only reason. – Konrad Rudolph Apr 11 '11 at 15:07
  • @KonradRudolph: VC++9 and GCC 4.5.4 at least will not do any extra resizes when given forward_iterators or better, which string iterators are, so the speed difference is basically nothing. – Mooing Duck Feb 24 '12 at 17:57
  • @MooingDuck Not sure how this works. There is no efficient general-purpose implementation of `std::distance` for forward iterators, is there? After all, you cannot subtract them. – Konrad Rudolph Feb 24 '12 at 18:29
  • @KonradRudolph: the constructor uses `std::distance` which _does_ use subtraction for random_access_iterators, which string's iterators are. Both VC++ and GCC use `std::distance` for forward iterators as well, (still faster than copying the data several times) and only do the multiple resizes for input_iterators. – Mooing Duck Feb 24 '12 at 19:24
  • @MooingDuck Yes, that actually makes sense. – Konrad Rudolph Feb 25 '12 at 17:33
6

You can get a const char* to the string using c_str:

std::string str = "foo bar" ;
const char *ptr = str.c_str() ;

If you need just a char* you have to make a copy, e.g. doing:

char *cpy = new char[str.size()+1] ;
strcpy(cpy, str.c_str());
Rune Aamodt
  • 2,551
  • 2
  • 23
  • 27
  • 3
    +1: Copying is one solution (so longer as your remember to `delete`!). – Oliver Charlesworth Apr 11 '11 at 11:04
  • But why use the unsafe function `strcpy` instead of `std::copy`, and why use a C-style string instead of a char array? – Konrad Rudolph Apr 11 '11 at 11:06
  • @Konrad: At some point, the OP will need to have a `char *` available for his legacy function. Are you suggesting copying into e.g. a `std::vector` first, and then getting `&v[0]`? – Oliver Charlesworth Apr 11 '11 at 11:07
  • @Oli Exactly. See my answer. In C++, there’s just no reason not to use C++ tools when available. – Konrad Rudolph Apr 11 '11 at 11:08
  • For safety strncpy(cpy, str.c_str(), str.length()) would be a better choice. – etipici Apr 11 '11 at 12:43
  • @etipici: That's wrong, isn't it? You're not copying the null terminator. And anyway `c_str()` is guaranteed to be zero-terminated, so there's no need for `strncpy` in any form. – TonyK Apr 11 '11 at 13:13
  • @TonyK: You're right, for this stiuation there is no need to use strncpy. Its just good practice i guess. About null terminator; since cpy has declared as an array of chars, it is initialized with 0's, so after strncpy it will have its null terminator at the end. – etipici Apr 12 '11 at 13:11
  • @etipici: a char array is not guaranteed to be zero initialized, so it's still not safe. – Rune Aamodt Apr 13 '11 at 06:35
  • 1
    @codebolt: If it is declared with **new**, it is guaranteed, because it would be created on the static store. I should have mention that. – etipici Apr 15 '11 at 07:24
2

As previous posters have mentioned if the called function does in fact modify the string then you will need to copy it. However for future reference if you are simply dealing with an old c-style function that takes a char* but doesn't actually modfiy the argument, you can const-cast the result of the c_str() call.

void oldFn(char *c) { // doesn't modify c }
std::string tStr("asdf");
oldFn(const_cast< char* >(tStr.c_str());
sfpiano
  • 369
  • 1
  • 8
1

There is c_str(); if you need a C compatible version of a std::string. See http://www.cppreference.com/wiki/string/basic_string/c_str

It's not static though but const. If your other function requires char* (without const) you can either cast away the constness (WARNING! Make sure the function doesn't modify the string) or create a local copy as codebolt suggested. Don't forget to delete the copy afterwards!

Jonas Bötel
  • 4,452
  • 1
  • 19
  • 28
0

Can't you just pass the string as such to your function that takes a char*:

func(&string[0]);
Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • This will probably wreak havoc if the called function modifies its argument. – Frank Schmitt Apr 11 '11 at 11:03
  • 1
    This is what `std::string::c_str()` is for. But that doesn't avoid the non-`const` issue... – Oliver Charlesworth Apr 11 '11 at 11:03
  • I thought c and c++ strings were treated differently. – Skeith Apr 11 '11 at 11:03
  • @Frank Schmitt: Care to explain such an example? Assuming that the function takes a `char*` by value it cannot possibly *modify* the argument (pointer), and regardless of whether it does modify the contents of the string (as long as it does not go beyond the end of the string, which there probably is size as a second argument) this should be fine. The only *grey* issue here is that `std::string` is allowed not to be contiguous or nul terminated by the standard, but there isn't any current implementation that uses ropes, and the upcoming standard requires contiguity. – David Rodríguez - dribeas Apr 11 '11 at 11:50
  • ... as of the nul termination, the fact is that `s[s.size()]` (when using the `const` version of `operator[]`) is required to return `'\0'`, and all implementations I know of will do the simple thing: allocate an extra element and set it to nul. This might not be the best option available, but from a practical standpoint it might just be acceptable, as applying `const_cast<>` to the result of `.c_str()` was recommended for C functions that do not modify the received `char*` contents... – David Rodríguez - dribeas Apr 11 '11 at 11:54
  • @Oli Charlesworth: There are two different problems, the solution offered by Tony does not have any issue with const-ness, as `&string[0]` will be non-const for non-const strings. It does however offer other problems --technically the string does not need to be contiguous/nul terminated, practically it is. Anyway, a few years ago everyone would have used a `const_cast` when calling a C function that did not modify the received string contents... – David Rodríguez - dribeas Apr 11 '11 at 11:57
  • @David "Wreak havoc" was too strong a statement - it will not cause undefined behaviour, but it will modify the std::string passed as argument, which I guess is not what the OP wants. – Frank Schmitt Apr 11 '11 at 12:01
  • In C++11 `func(&string[0])` is perfectly fine, so long as the function does not try to write to the location of the null terminator or later. (Of course, the concern still applies that if the function does modify the string, and OP wants to retain the initial value of the string, he will need to make a copy). – M.M Jul 23 '14 at 23:00