65

Doing this in C++

char* cool = "cool";

compiles fine, but gives me a warning:

deprecated conversion from string constant to char*.

I would never willfully use a C-style string over std::string, but just in case I'm asked this question:

is it bad practice to declare a C-style string without the const modifier? If so, why?

Maraboc
  • 10,550
  • 3
  • 37
  • 48
Coffee Maker
  • 1,543
  • 1
  • 16
  • 29

8 Answers8

60

Yes, this declaration is bad practice, because it allows many ways of accidentally provoking Undefined Behavior by writing to a string literal, including:

cool[0] = 'k';
strcpy(cool, "oops");

On the other hand, this is perfectly fine, since it allocates a non-const array of chars:

char cool[] = "cool";
aschepler
  • 70,891
  • 9
  • 107
  • 161
  • 3
    Indeed, the latter (ok) case is what *most* people mean by the term "C-style string", which is admittedly ambiguous. – Chris Dodd Jul 25 '16 at 17:59
  • 15
    @ChrisDodd I'm not so sure about that. The only meaning I've ever seen for "C-style string" is "array of characters where zero marks the end", which applies equally to `char *cool = "cool";`, `const char *cool = "cool";`, `char cool[] = "cool";` and `const char cool[] = "cool";`, and doesn't to all of `char cool = "cool\0";`, where only the first five of the six characters form a C-style string. I'm not able to find a different definition even with the help of Google. –  Jul 25 '16 at 22:37
  • 2
    how is `char cool[] = "cool";` different from `char* cool = "cool";`? I would have thought those were basically identical. – Dave Cousineau Jul 26 '16 at 02:28
  • 6
    The former creates an array and initialises it's elements using the string literal. The latter creates a pointer and sets it to point at the first character in the string literal. So it's safe to write in the former case but not in the latter case. – plugwash Jul 26 '16 at 03:53
  • 1
    @plugwash maybe I'm being dumb, but in both cases don't you end up with `*cool == 'c'` and subsequent addresses containing subsequent characters until `*(cool+4)=='\0'`? – Liam Jul 26 '16 at 10:54
  • Sure but that doesn't mean they are the same, it's all about what memory the string is in and therefore what constraints on access and lifetime apply. `*cool = 'f'` is safe with `"char [] cool = "cool"` but unsafe with `"char * cool = "cool"` `return cool` is safe with `"char * cool = "cool"` but unsafe with `"char [] cool = "cool"` (assuming cool is a local variable) – plugwash Jul 26 '16 at 12:30
  • 4
    @Liam, the actual reason is that when you do char* cool = "cool", you only take address of "cool" literal, which may have been allocated in a read-only section of memory and writing there may cause segmentation faults or undefined behavior(suppose the compiler was clever enough to use same literal for all occurences of "cool" in your code). When you do char[] cool = "cool", characters are stored in a read-write section of memory so writing is safe. – poe123 Jul 26 '16 at 13:02
  • @Liam If you do `char* c = "cool";`, that makes `c` a pointer to a string *constant*. If you do `char[] c = "cool";`, that makes `c` an array *variable*. Constants cannot be modified, that's what makes them constants. In the former case, `*c = 'f';` tries to modify part of the thing `c` points to, which is a constant. Bad. In the latter case, it modifies the contents of an array variable, which is fine. – David Schwartz Jul 26 '16 at 14:57
17

Yes, in C++ you should always refer to string literals with variables of type const char * or const char [N]. This is also best practice when writing new C code.

String literals are stored in read-only memory, when this is possible; their type is properly const-qualified. C, but not C++, includes a backward compatibility wart where the compiler gives them the type char [N] even though they are stored in read-only memory. This is because string literals are older than the const qualifier. const was invented in the run-up to what's now called "C89" -- the earlier "K&R" form of the language did not have it.

Some C compilers include an optional mode in which the backward compatibility wart is disabled, and char *foo = "..."; will get you the same or a similar diagnostic that it does in C++. GCC spells this mode -Wwrite-strings. I highly recommend it for new code; however, turning it on for old code is liable to require an enormous amount of scutwork for very little benefit.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • 1
    In C, I'm under the impression that `-Wwrite-strings` makes you cast the `const` away if you use a library call that takes a `char *`. I prefer not using the flag in those situations. – yellowantphil Jul 25 '16 at 17:48
  • 1
    @yellowantphil Yes, but because you'd have to do that anyway when calling the function from C++, most libraries with a C interface have by now been fixed so that you don't need to cast anything. (That is, the arguments are declared `const char *` unless they're actually going to be modified.) – zwol Jul 25 '16 at 17:51
  • I ran into that problem with [`execv`](http://stackoverflow.com/questions/36925388) and I'm still not entirely sure why POSIX went with a `char *const` for the second argument. – yellowantphil Jul 25 '16 at 18:34
  • "String literals are stored in read-only memory" --> read-only memory is not required for a conforming compiler. Writing to these is UB - it may work write, it may fail quietly, it may stop the program, etc. – chux - Reinstate Monica Jul 25 '16 at 18:47
  • @yellowantphil There's an explanation for that buried in the [official specification for `execvp`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html#tag_16_111_08) (look for the table) but you may also need to read http://c-faq.com/ansi/constmismatch.html to understand just how nasty the "limitation in C" they're talking about really is. – zwol Jul 25 '16 at 19:34
  • @chux That level of pedanticism is not useful to a poster at the OP's level of understanding, but I will add "when possible" to the sentence. – zwol Jul 25 '16 at 19:35
  • @zwol not to be meta-pedantic, but the word is [pedantry](https://books.google.com/ngrams/graph?content=pedantry%2Cpedanticism&year_start=1800&year_end=2000&corpus=15&smoothing=3&share=&direct_url=t1%3B%2Cpedantry%3B%2Cc0%3B.t1%3B%2Cpedanticism%3B%2Cc0) – Steve Cox Jul 25 '16 at 20:37
14

It's bad. It's very bad. To the point this isn't possible to do anymore in C++11.

Modifying the memory of a string literal is undefined behaviour.

milleniumbug
  • 15,379
  • 3
  • 47
  • 71
  • yea, a const string is locked and i came accross this many times, the behavior of const is almost similar to the behavior of #define. – Abr001am Nov 25 '17 at 20:30
13

First, char* cool = "cool"; is not standard C++. A string literal has the type of const char[n]. So the above line of code breaks const-correctness and should not compile. Some compilers like GCC allow this but issue a warning as it is a hold over from C. MSVC will issue a error since it is a error.

Second, why not let the compiler work for you? If it is marked const then you will get a nice compiler error if you accidentally try to modify it. If you do not then you can get a really nasty run time error which can be much harder to find.

TylerH
  • 20,799
  • 66
  • 75
  • 101
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
7

It is bad because string constants might be contained only once per binary (keyword: stringtable, .strtab). E.g. in

char *cool = "cool";
char *nothot = "cool";

both variables can point to the same memory location. Modifying the contents of one of them might alter the other too, so that after

strcpy(nothot, "warm");

your cool becomes "warm".

In short, it is undefined behaviour.

wizzwizz4
  • 6,140
  • 2
  • 26
  • 62
ensc
  • 6,704
  • 14
  • 22
6

It is a string literal, therefore it should be constant as memory might be located in read only section. If you have char cool[] = "cool"; then it's not a problem, the memory is yours.

KIIV
  • 3,534
  • 2
  • 18
  • 23
2

char* cool = "cool"

"cool" will be stored in a read only block (generally in data segment) that is shared among functions. If you try to modify the string "cool" by the point cool you will get a error such as segment error when the program is running. If you use const char* cool = "cool", you will get a error when compile if you try to modify the string.
You can read this page for more information http://www.geeksforgeeks.org/storage-for-strings-in-c/

zhangke
  • 178
  • 1
  • 2
  • 7
0

Its a good practice to write const for strings(especially when you used a string literal) but in C it hardly makes a difference ,it will throw you a warning in c++ but no warning in c,also remember some compilers assume .c extension simply as c but .C as c++,so be careful at such points.otherwise it is a good practice to use const with the case of strings ,so by mistake you don't change the string or try to change a string literal which is stored in read only memory.

LocalHost
  • 910
  • 3
  • 8
  • 24