Is it bad to declare a C-style string without const? If so, why?
Asked Answered
L

8

67

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?

Lyontine answered 25/7, 2016 at 17:37 Comment(11)
BTW this is C not C++Instructor
When compiled the strings are usually located in a read-only memory segment on most modern architectures, just as many other constantsBonaparte
Please pick a language. The answers are different for each language.Guiscard
This question is appropriately tagged both C and C++, because it is about a difference between C and C++.Kleptomania
@Kleptomania Then it should ask that. No where in the Question does it ask why is it different from C. Might be implied but it should be explicit IMHO.Guiscard
@Guiscard To my mind it is so strongly implied that it does not need to be explicit -- or, to put it another way, I would consider it improper to answer this question without spelling out that this is a place where C and C++ are different.Kleptomania
@NathanOliver: actually, for this particular question, the answer is the same for both languages, though the term "C-style string" means many different things to different people. I for one would never call the OP's example a "C-style string", even in C.Edaedacious
That is a Issue. To me a c-string/c style string is a char[].Guiscard
Not a duplicate, but very related: #1704907Dashed
@zwol: Looks like a pure C++ question to me.Cilo
more like char* cool = "not cool"; :-)Dracaena
M
62

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";
Mer answered 25/7, 2016 at 17:40 Comment(8)
Indeed, the latter (ok) case is what most people mean by the term "C-style string", which is admittedly ambiguous.Edaedacious
@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.Corliss
how is char cool[] = "cool"; different from char* cool = "cool";? I would have thought those were basically identical.Directly
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.Whoreson
@Whoreson 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'?Pater
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)Whoreson
@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.Encamp
@Pater 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.Mulcahy
K
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.

Kleptomania answered 25/7, 2016 at 17:45 Comment(7)
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.Filipe
@Filipe 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.)Kleptomania
I ran into that problem with execv and I'm still not entirely sure why POSIX went with a char *const for the second argument.Filipe
"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.Overawe
@Filipe There's an explanation for that buried in the official specification for execvp (look for the table) but you may also need to read c-faq.com/ansi/constmismatch.html to understand just how nasty the "limitation in C" they're talking about really is.Kleptomania
@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.Kleptomania
@Kleptomania not to be meta-pedantic, but the word is pedantryPetulance
S
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.

Schick answered 25/7, 2016 at 17:39 Comment(1)
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.Robinrobina
G
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.

Guiscard answered 25/7, 2016 at 17:41 Comment(4)
No one mentioned that casting the const away is possible and still undefined behavior can occur. It's of course something the programmer will have to do so explicitly that we can blame such programmer for all the issues caused by modifying a string literal.Inadvertence
According to this, the type of string literal is not const..Akin
@EugeneSh. Fixed. I only saw the C++ tag and it is not standard C++.Guiscard
@EugeneSh.: That's C.Cilo
D
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.

Demos answered 25/7, 2016 at 17:51 Comment(0)
W
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.

Waly answered 25/7, 2016 at 17:40 Comment(0)
L
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/

Leclaire answered 26/7, 2016 at 6:27 Comment(0)
B
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.

Butyraldehyde answered 13/8, 2018 at 23:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.