I'm working in a C, and C++ program. We used to be compiling without the make-strings-writable option. But that was getting a bunch of warnings, so I turned it off.
Then I got a whole bunch of errors of the form "Cannot convert const char* to char* in argmuent 3 of function foo". So, I went through and made a whole lot of changes to fix those.
However, today, the program CRASHED because the literal "" was getting passed into a function that was expecting a char*, and was setting the 0th character to 0. It wasn't doing anything bad, just trying to edit a constant, and crashing.
My question is, why wasn't that a compiler error?
In case it matters, this was on a mac compiled with gcc-4.0.
EDIT: added code:
char * host = FindArgDefault("EMailLinkHost", "");
stripCRLF(linkHost, '\n');
where:
char *FindArgDefault(char *argName, char *defVal)
{// simplified
char * val = defVal;
return(val);
}
and
void stripCRLF(char *str, char delim)
{
char *p, *q;
for (p = q = str; *p; ++p) {
if (*p == 0xd || *p == 0xa) {
if (p[1] == (*p ^ 7)) ++p;
if (delim == -1) *p = delim;
}
*q++ = *p;
}
*q = 0; // DIES HERE
}
This compiled and ran until it tried to set *q to 0...
EDIT 2:
Most people seem to be missing the point of my question. I know why char foo[] = "bar" works. I know why char * foo = "bar"; doesn't work.
My question is mostly with respect to passing parameters. One thing that occures to me is "Is it possible that this is a C vs C++ issue?" because I have some .c files and some .cpp files, and it's quite possible that C allows it, but C++ doesn't... or vice versa...
To answer the question why this conversion is legal (although deprecated). Well there was a time, when there was no const keyword in the C language and people managed to produce a bit of code during that time. The designers of C++ must have figured out, that it wasn't a good idea to upset so many people, by breaking their code.
Since the
stripCRLF
function modifies a string in place but doesn't do anything with it or return any value, passing a string literal to it is essentially a no-op and should be considered a bug. You can either solve this by having the function modify and return a copy of the string, or by setting stricter warning flags to help detect when this happens.If you want gcc to alert you of things like this, turn on the
-Wwrite-strings
compiler option. This will force the compiler to warn you if a string constant is converted to a non-constantchar*
. Also possibly useful is the-Wcast-qual
option; this should emit a warning whenever a pointer is cast in a way that removes a type qualifier (in your case, theconst
got removed). If you want these messages to be made more strongly, use-Werror
to turn all warnings into errors.The other point of contention is the
FindArgDefault
function. As provided, the function signature should more accurately useconst char*
instead ofchar*
for the return and parameter types. This should cause the compiler to complain when the return value is assigned to achar*
(if the-Wcast-qual
option is used). Since you didn't post the complete function, this might not be a valid change to make. If either string is modified inside the function then the corresponding parameter must remain achar*
, but in that event passing a string literal as the argument should generate a compiler warning (use-Wwrite-strings
).By the way, your
stripCRLF
function is vulnerable to problems when a NULL pointer is passed in. Also, did you mean to sayif (delim == -1)
, or should that be!=
?Edit: After seeing more information about the error messages the OP was getting, I removed parts of the original post that were off-topic and added some additional comments.
Edit2: I tested the following simplified version of your program:
When I compiled with
gcc -Wall test.c -o test.o
, I got zero compiler warnings or errors.When I compiled with
gcc -Wwrite-strings -Wall test.c -o test.o
, I gotI definitely think that the
-Wwrite-strings
compiler option is the one you want to enable to warn you about this sort of problem.The standard specifies a special rule allowing the literal-to-
char*
conversion which quietly dropsconst
qualification. (4.2/2):The C++0x standard takes that deprecation further… this nonsense rule is removed entirely from the upcoming standard.
The
const char*
tochar*
error must be a result of converting a literal to aconst char*
first.I agree completely with the other answers, I just want to add that g++ (at least version 4.4) actually catches these deprecated conversions as warnings at any warning level (if previous versions do not do this by default, probably you have to raise the warning level):
Moreover, there's some interesting thing going on under the hood: if I compile it without optimization, it "just does what the code says", so it crashes, since it tries to write to a read-only memory location:
but, if you turn on the optimizer, there's no crash:
I suppose that this is due to some magic optimization trick, but I don't understand why is it applied; any idea?
Addendum
Hey, be careful not to confuse the two things: with
you are declaring a pointer to char, and you assign to it the address of the literal "bar", which is actually stored in some read-only memory location (usually it's a part of the executable that is mapped in memory). Instead, with
you are declaring and allocating RW memory (on the stack or somewhere else, depending from the context) for an array of chars, which is initialized with the "bar" value, but it is not related to the string table at all, and it's perfectly legit to change that string.
Using a string literal to initialize a
char *
pointer in C++ is a deprecated feature, but nevertheless it is legal. It is not an error. It is your responsibility to make sure that no modification attempts are made through such a pointer.In other words, you must be misunderstanding something about the compilation errors you got earlier. I don't think you ever got any errors for such an initialization/assignment. The "Cannot convert const char* to char*" errors you mention in your question must have been produced by something else.
Note, that the fact that you can initialize
char *
pointer with a string literal does not mean that you can use any arbitraryconst char *
value to initialize achar *
pointer. This codewill produce an error, while this
will not. The aforementioned deprecated feature applies to string literals only, not for all
const char *
pointers.It really depends how you "went through and made a whole lot of changes to fix those."
If you just downcast the string literal to a
char*
then you're telling the compiler not to catch that error. You need to make a copy if you're going to modify it. Otherwise declare your function interfaces to take aconst
so that the compiler can check these for you.