Why pass a string literal to a char * argument only occasionally with a compiler error?

I work in C and C ++. Previously, we compiled without the option to write a string. But it got 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 functions foo". So, I went through and made many changes to fix this.

However, today the program is CRASHED, because the literal "" was passed to a function expecting char * and set the 0th character to 0. This did nothing wrong, just tried to edit the constant and the failure.

My question is: why was the compiler error not like that?

In case it matters, it was on a mac compiled with gcc-4.0.

EDIT: code added:

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 } 

Compiled and run until it tried to set * q to 0 ...

EDIT 2:

Most people seem to be missing my question. I know why char foo [] = "bar" works. I know why char * foo = "bar"; does not work.

My question is mainly related to passing parameters. One thing that comes up with me is "Is it possible that this is a C vs C ++ problem?" because I have some .c files and some .cpp files, and it is entirely possible that C allows this, but C ++ does not ... or vice versa ...

+6
c ++ c char const string-literals
source share
6 answers

The standard specifies a special rule allowing literal-to char* conversion, which quietly reduces the qualification of const . (4.2 / 2):

A string literal (2.13.4), which is not a wide string literal, can be converted to a char pointer pointer rvalue; a wide string literal can be converted to an rvalue of type "pointer to wchar_t". In any case, the result is a pointer to the first element of the array. This conversion is considered only when there is an explicit corresponding target pointer type, and not when a general conversion from lvalue to rvalue is required. [Note: this conversion is outdated. See Appendix D.]

The C ++ 0x standard continues this fatigue ... this rule of meaninglessness is completely removed from the future standard.

The const char* to char* error should be the result of converting the literal to const char* .

+8
source share

Using a string literal to initialize a char * pointer in C ++ is an obsolete function, but it's legal anyway. It's not a mistake. You are responsible for ensuring that no attempt to change is made using such a pointer.

In other words, you should misunderstand something about the compilation errors that you received earlier. I do not think you have ever had errors for such initialization / assignment. "Cannot convert const char * to char *" the errors you mention in your question should have been created by someone else.

Note that just because you can initialize a char * pointer with a string literal does not mean that you can use any arbitrary const char * value to initialize a char * pointer. This code

 const char *pc = "A"; char *p = pc; 

will result in an error, and

 char *p = "A"; 

will not. The above deprecated function applies only to string literals, and not to all const char * pointers.

+6
source share

I completely agree with the other answers, I just want to add that g ++ (at least version 4.4) really catches these obsolete conversions as warnings at any warning level (if previous versions don't do this by default, maybe you need to raise the level warnings):

 #include <iostream> using namespace std; void WithConst(const char * Str) { cout<<Str<<endl; } void WithoutConst_NoEdit(char * Str) { cout<<Str<<endl; } void WithoutConst_Edit(char * Str) { *Str='a'; cout<<Str<<endl; } int main() { WithConst("Test"); WithoutConst_NoEdit("Test"); WithoutConst_Edit("Test"); return 0; } 

 matteo@teoubuntu :~/cpp/test$ g++ --version g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3 Copyright (C) 2009 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. matteo@teoubuntu :~/cpp/test$ g++ -O3 lit_const_corr.cpp -o lit_const_corr.x lit_const_corr.cpp: In function 'int main()': lit_const_corr.cpp:24: warning: deprecated conversion from string constant to 'char*' lit_const_corr.cpp:25: warning: deprecated conversion from string constant to 'char*' matteo@teoubuntu :~/cpp/test$ g++ -O3 -Wall lit_const_corr.cpp -o lit_const_corr.x lit_const_corr.cpp: In function 'int main()': lit_const_corr.cpp:24: warning: deprecated conversion from string constant to 'char*' lit_const_corr.cpp:25: warning: deprecated conversion from string constant to 'char*' matteo@teoubuntu :~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x lit_const_corr.cpp: In function 'int main()': lit_const_corr.cpp:24: warning: deprecated conversion from string constant to 'char*' lit_const_corr.cpp:25: warning: deprecated conversion from string constant to 'char*' 

In addition, there is something interesting that happens under the hood: if I compile it without optimization, it โ€œjust does what the code saysโ€, so it crashes because it tries to write to a memory only for reading:

 matteo@teoubuntu :~/cpp/test$ g++ -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x lit_const_corr.cpp: In function 'int main()': lit_const_corr.cpp:24: warning: deprecated conversion from string constant to 'char*' lit_const_corr.cpp:25: warning: deprecated conversion from string constant to 'char*' matteo@teoubuntu :~/cpp/test$ ./lit_const_corr.x Test Test Segmentation fault 

but , if you turn on the optimizer, there is no failure:

 matteo@teoubuntu :~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic lit_const_corr.cpp -o lit_const_corr.x lit_const_corr.cpp: In function 'int main()': lit_const_corr.cpp:24: warning: deprecated conversion from string constant to 'char*' lit_const_corr.cpp:25: warning: deprecated conversion from string constant to 'char*' matteo@teoubuntu :~/cpp/test$ ./lit_const_corr.x Test Test Test 

I believe this is due to some kind of magic optimization trick, but I donโ€™t understand why it is applied; any idea?


Adding

When I declare char * foo = "bar", it actually complains. But when I declare char foo [] = "bar", it does not

Hey, be careful not to confuse two things: with

 char * foo = "bar"; 

you declare a pointer to char, and you assign it the address of the literal โ€œbarโ€, which is actually stored in some read-only memory location (usually this is the part of the executable that is mapped to Memory). Instead with

 char foo[]="bar"; 

you declare and allocate RW-memory (on the stack or somewhere else, depending on the context) for an array of characters that is initialized with the value "bar", but it is not associated with the string table at all, and it is completely natural to change this string.

+2
source share

Actually, it depends on how you "went through and made a lot of changes to fix them."

If you just flush the string literal to char* , then you are not telling the compiler to catch this error. You need to make a copy if you are going to modify it. Otherwise, declare the functional interfaces to accept const , so that the compiler can test them for you.

+1
source share

To answer the question of why this conversion is legal (although not recommended). It was a good time when the const keyword was not in C, and people managed to create some code during this time. C ++ designers must have figured out that it wasnโ€™t very nice to upset so many people breaking their code.

+1
source share

Since the stripCRLF function changes the string in place, but does nothing with it or returns no value, passing the string literal to it is essentially non-op and should be considered an error. You can solve this by changing the function and returning a copy of the string, or by setting stricter warning flags to determine when this will happen.

If you want gcc to warn you about such things, enable the -Wwrite-strings compiler -Wwrite-strings . This will cause the compiler to warn you if the string constant is converted to mutable char* . It is also useful to use the -Wcast-qual option; this should give a warning whenever the pointer is passed in a way that removes the type classifier (in your case, the remote const ). If you want these messages to be made stronger, use -Werror to turn all warnings into errors.

Another argument is the FindArgDefault function. As indicated, the function signature should more accurately use const char* instead of char* for return types and parameters. This should make the compiler complain when the return value is assigned to char* (if the -Wcast-qual option is used). Since you have not published the full feature, this may not be valid. If any string is modified inside the function, then the corresponding parameter must remain char* , but in this case passing a string literal, since the argument should generate a compiler warning (use -Wwrite-strings ).

By the way, your stripCRLF function stripCRLF vulnerable to problems when passing a NULL pointer. Also, did you mean to say if (delim == -1) or should it be != ?

Edit: After looking at more information about the errors that the OP was getting, I deleted the parts of the original message that were off topic and added some additional comments.

Edit2: I tested the following simplified version of your program:

 char *FindArgDefault(char *argName, char *defVal) { char * val = defVal; return(val); } int main (void) { char * host = FindArgDefault("EMailLinkHost", ""); return (int)(host); } 

When I compiled with gcc -Wall test.c -o test.o , I received zero warnings or compiler errors.

When I compiled with gcc -Wwrite-strings -Wall test.c -o test.o , I got

test.c: In the "main" function:

test.c: 10: warning: passing arg 1 from 'FindArgDefault' discards qualifiers from the target pointer type

test.c: 10: warning: passing arg 2 from 'FindArgDefault' discards qualifiers from the target pointer type

I definitely think that the -Wwrite-strings compiler -Wwrite-strings is the one you want to enable in order to warn you about this problem.

+1
source share

All Articles