views:

254

answers:

6

I'm a bit fuzzy on the basic ways in which programmers code differently in C and C++. One thing in particular is the usage of strings in C++ over char arrays, or vice versa. So, should I use strings or char arrays, in general, and why?

+14  A: 

In C++ you should in almost all cases use std::string instead of a raw char array.

std::string manages the underlying memory for you, which is by itself a good enough reason to prefer it.

It also provides a much easier to use and more readable interface for common string operations, e.g. equality testing, concatenation, substring operations, searching, and iteration.

James McNellis
Ease of use is the #1 reason for me.
Goose Bumper
You could mention the primary exception being interfacing with existing C libraries, and even then ones that only require input can just take a const char* from your_string.c_str()
Mark B
+1  A: 

Use std::string. You will have less problems (I think almost none, at least none come to my mind) with buffer sizes.

swegi
+1  A: 

C has char[] while c++ has std::string too...

I commonly hear that one should "Embrace the language" and, following that rule, you should use std::string...

However, its pretty much up to what library are you using, how does that library want you to express your strings, stuff like that.

std::string is a container class, and inside it, is a char[]
If you use std::string, you have many advantages, such as functions that will help you [compare, substr, as examples]

ItzWarty
And if the library is "lame" and doesn't support strings, there's always std::string::c_str()!
vgm64
+1  A: 

Others have put it. Use the std::string stuff wherever possible. However there are areas where you need char *, e.g if you like to call some system-services.

Friedrich
There is `std::string::c_str()` for this reason.
swegi
@swegi: Many APIs expect a `char*` and not a `const char*` (e.g., most, if not all, of the WinAPI). In these cases, though, it is a good idea to use a `std::vector<char>` and convert between it and a `std::string` as you need to.
James McNellis
@James McNellis that is what const_cast is for.
mch
@mch: No. There is no guarantee that a function taking a `char*` does not modify the string. If the function does attempt to modify the string, you run headlong into the land of undefined behavior.
James McNellis
+4  A: 

If you're modifying or returning the string, use std::string. If not, accept your parameter as a const char* unless you absolutely need the std::string member functions. This makes your function usable not only with std::string::c_str() but also string literals. Why make your caller pay the price of constructing a std::string with heap storage just to pass in a literal?

Ben Voigt
I don't agree with that. On the contrary: always take a `string` (or at least a templated `basic_string<Ch,Tr>`) because it's just plain better than `char*`. If the user has a raw array of `char` then that works too: `string` is convertible from `char*`. The other way around doesn't work. `char` is here only for backward compatibility with C code, for accepting a single raw character and for the cases where you specifically need a data element of size 1.
wilhelmtell
`string` is not convertible from `char*`. `string` is constructible from `char*`, and causes heap allocation and fragmentation. (Please don't downvote unless you have a response to the last sentence of my answer.) And the other way around works just fine with the `c_str()` member function (which is what I already mentioned in my answer). And pointers are neither there only for backward compatibility, nor to hold single values. Quite the opposite, they make great iterators and all the standard library algorithms are carefully designed to work correctly if the iterators are pointers.
Ben Voigt
@Ben: +1 I like this idea, though I've not seen it used much in practice. I guess it depends on how you end up using the string. Technically, `string` _is_ convertible from `char*`: a non-explicit, single-parameter constructor is a "converting constructor" and falls under the category of user-defined conversions. That doesn't mean that calling it is cheap, of course.
James McNellis
Guys, wow, you're dealing with premature optimization here. _All_ proper functions will take a string by const reference unless they need a copy of it (in which case you won't save anything by having it take a `char*`). So you _won't_ gain performance by having it take a `char*` instead. There is _no_ reason to use `char*` when you need a string -- unless your primitive strings are fixed on the stack or compiled in and the profiler said that's how they should be. Advising to use a `char*` over a `string` is roughly never a good advice if the profiler didn't say the same thing.
wilhelmtell
@wilhelmtell: Taking a string by const reference doesn't prevent you from having to construct a temporary string object in order to pass a string literal. I think this sounds like a reasonable, easy, non-intrusive, non-readability-reducing "premature" optimization.
James McNellis
Well, I think it _is_ intrusive if I need to call `c_str()`, and it _is_ reducing readability if I need to stop and ask "why is this a `char*`?". Anything at all that would take a re-allocation with a `string` would also take a re-allocation with a C string. The corner-cases in which you can squeeze out some performance aren't worth formulating a rule of thumb for. You should identify all of these cases with a profiler and strike them individually.
wilhelmtell
@James why would you construct a temporary to pass to a function that takes a const reference? This should never happen -- unless you're working with a `char*`. If your team constantly works with a `string` and you're working with a `char*` then I'm sorry, you're working _against_ your team. If your team works with a `char*` then all my arguments are void here: do as the Romans do.
wilhelmtell
Personally, I am less concerned with whether or not it is a potential optimization than with whether or not it is a detail users of my code need to be concerned with. What I do with their strings is my business; why confuse the issue by having half of the functions require `.c_str()` and half of them not?
Dennis Zickefoose
And really, what fraction of your strings are literals anyhow? Accepting a `const char*` provides no benefit if you don't pass a literal, because otherwise you're dealing with the heap either way. Except maybe if you make heavy use of stack based arrays. And that is definitely not something you want to do without a good justification.
Dennis Zickefoose
Actually, `const char*` can not only point to a literal without conversion, but also into a `std::vector`, into the middle of a `std::string`, a global pointer or input argument initialized from a literal, etc. And in the software I write, nearly all usage of strings falls into one of those categories. The proportion would be less with localization, but not that much less. Argument parsing doesn't get localized, neither do log files and error codes (in any sane system). And with search engines, it's a toss-up whether even error messages should be localized.
Ben Voigt
Furthermore, depending on what mechanism you use for loading localized strings, you might again end up with a large number of `const char*` into a single text resource. And we haven't even touched on passing strings across DLL boundaries.
Ben Voigt
As far as premature optimization vs profiling is concerned, can any of you recommend a profiler that shows the sources of heap fragmentation?
Ben Voigt
A: 

As is the case with everything what you choose depends on what you're doing with it. std::string has real value if you're dealing with string data that changes. You can't beat char[] for efficiency when dealing with unchanging strings.

Jay