warning: deprecated conversion from string constant to 'char*'|

Pages: 12
Apr 5, 2011 at 8:08pm
Help me out guys. The code I have shows this warning a lot when using gcc. No other compiler for this code does this warning. So I am wandering what a Quoted string is defaulting to?

1
2
3
4
5
6
CString::CString(char *str); // the basic constructor in my code

CString strSomeString("A String contents"); // this kind of line gets the warning.

// So what does "A Strings contents" default to in gcc?
Apr 5, 2011 at 8:13pm
const char*
Apr 5, 2011 at 8:18pm
and this is a deprecation? I would expect a conversion warning other than this I guess.
Apr 5, 2011 at 8:23pm
The C++ standard explicitly allows a conversion from a string literal to char* (even though a string literal is a const array) for compatibility reasons, however it's deprecated, just as gcc is telling you.

Edit: string literal or not, your code is not const correct. I wouldn't expect the constructor to try and modify str, so it should have been const char* in the first place. That would have saved you the warnings when trying to pass string literals too.
Last edited on Apr 5, 2011 at 8:31pm
Apr 5, 2011 at 8:43pm
I won't argue with your point. However, you never know what the code will do in a constructor. Never make any assumption you know what a programmer may or may not do in their code. For all you know that the constructor could convert the information to a working form for the class.

The other point you are making is I need two constructors for the same idea in code?

1
2
CString::CString(char *str); // the basic constructor in my code
CString::CString(const char *str);// the other form. 


Because you're assuming I won't do something like this:
1
2
3
4
5
6
7

char *someString = "Anything I want";

// some work in this form....

CString strString(someString);
Last edited on Apr 5, 2011 at 8:50pm
Apr 5, 2011 at 8:50pm
Do you necessarily need the non const one? Do you modify the value of the argument in the constructor?
Apr 5, 2011 at 8:55pm
char *someString = "Anything I want";

Well see.. this won't work either, for the same reason you're getting the above error. String literals are not char*s, they're const char*s.

This would work:

1
2
const char* someString = "Anything";
CString strString(someString);


What's more... char pointers can be implicitly cast to const char pointers. So even this would work:

1
2
3
char buffer[100] = "A buffer";

CString strString(buffer);  // assuming the CString ctor takes a const char*, this still works 


Basically, const char* will work for you needs unless you plan on changing the string data in the ctor. And if you're planning to do that, you can't use string literals anyway (but you shouldn't do that!)
Apr 5, 2011 at 8:58pm
Never make any assumption you know what a programmer may or may not do in their code.

A class called CString has no business modifying a C string you pass to it. Period.

For all you know that the constructor could convert the information to a working form for the class.

Possibly. But it will have to do that on a copy of said information, not on the original.

Trying to modify a string literal via a non-const char pointer is undefined, by the way.


The other point you are making is I need two constructors for the same idea in code?

No, there is no need for the non-const one. A non-const pointer can be converted to a const pointer just fine, just the other way round is not possible.
Apr 5, 2011 at 9:22pm
I have always end up with arguments with the compilers because I need to work with a char* coming from a const char*, with more than on function in this class, and end up with a type conversion error and you trying to raise an eyebrow why the type conversion error. Especially if I am doing character by character scans or something similar.


for example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const char *pSomeString = "Anything I want is not Constant";  // the pointer is but the string literal isn't.
// I can modify this block of memory as long as I don't change its size.
       char *pCurrent = pSomeString; // would generate some error or warning
       // when this is perfectly legal code.
       While(*pCurrent)
       { 
                // do work here, modify or not modify
               // depending on what I do here makes that pointer not constant or constant
               *pCurrent = *pCurrent +10;
               pCurrent++;
       }

       std::cout << pSomeString << std::endl


Arthor, Remember in C++ I can name things anything I want. Reading something into a class title is big leap of faith. So basically you are making an assumption, even though I try to name something what people expect to be there.

And Yes the Three constructors I have for class
1
2
3
4

        CString();
        CString(CString &cString);      // Copy Constructor
        CString(char *pString);         // Char ptr.. 
Last edited on Apr 5, 2011 at 9:40pm
Apr 5, 2011 at 9:33pm
The string literal is constant. You cannot modify a string literal. Even if the compiler would let pass line 3, this would result in undefined behavior.
I already said that in my previous post, though.
Apr 5, 2011 at 9:53pm
You don't know pointers very well.....

I guess I could do the same things in assembly.

I am only changing the contents at every pointer location not the pointer.. The pointer is the only thing that is constant. The memory location never changes, but I can change what's in the memory.

this is what you are saying these two things are the same.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
char *pSomeString = "Some String."
const char  *pSomeOtherSTring = "Some other string";
// since those are the same thing to the compiler...

// I can modify this block of memory as long as I don't change its size.
       char *pCurrent = pSomeString; // would generate some error or warning
//       char *pCurrent = pSomeOtherString; // this would generate the error I would expect.
       // when this is perfectly legal code.
       While(*pCurrent)
       { 
                // do work here, modify or not modify
               // depending on what I do here makes that pointer not constant or constant
               *pCurrent = *pCurrent +10;
               pCurrent++;
       }

       std::cout << pSomeString << std::endl


// the "quoted string" is constant in both cases. 


I have seen this style of coding for 20 years.. Its called usually pointer math.
Apr 5, 2011 at 9:57pm
@Azagaros: a literal is constant by definition. Think of numeric literals. You can't change the value of the literal 1 any more than you can change the value of the literal "Hello, world!". What you call "quoted string" is a string literal.
Apr 5, 2011 at 10:08pm

I am only changing the contents at every pointer location not the pointer.

That happens to be the part that is not allowed. You can change the pointer as much as you want, it's not const. It points to a constant object, which is a difference.

The pointer is the only thing that is constant.

No, see above.

but I can change what's in the memory.

Not if it is read-only memory.

There's no real reason you couldn't just look up the relevant parts in the C++ standard yourself, but here goes:

2.13.4.1 A string literal is a sequence of characters (as defined in 2.13.2) surrounded by double quotes, optionally beginning with the letter L, as in "..." or L"...". A string literal that does not begin with L is an ordinary string literal, also referred to as a narrow string literal. An ordinary string literal has type “array of n const char” and static storage duration (3.7), where n is the size of the string as defined below, and is initialized with the given characters.
[...]


4.2.2 A string literal (2.13.4) that is not a wide string literal can be converted to an rvalue of type “pointer to char”; a wide string literal can be converted to an rvalue of type “pointer to wchar_t”. In either case, the result is a pointer to the first element of the array. This conversion is considered only when there is an explicit appropriate pointer target type, and not when there is a general need to convert from an lvalue to an rvalue. [ Note: this conversion is deprecated. See Annex D. — end note ] For the purpose of ranking in overload resolution (13.3.3.1.1), this conversion is considered an array-to-pointer conversion followed by a qualification conversion (4.4).


7.1.5.1.4 Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.


As it is, your above code will cause a segmentation fault on most platforms, as data like string literals are in read-only sections of memory.
You also need to do some reading on const correctness. The reference passed to a copy constructor should virtually always be const. The rest has been discussed already.
Last edited on Apr 5, 2011 at 10:14pm
Apr 5, 2011 at 10:24pm
String literal are not constant, unless stated so. Even the c++ standard published as of 2010, unless I put const in front of it, it should be a modifiable point in memory with a constant pointer. I should see no warning what so ever for this stuff. What is the true meaning of the warning to gcc? I don't see it with any other compiler.

char *pSomeString = "This string is modifiable"; // the pointer is constant the string isn't.
const char* pSomeOtherString = "This String isn't modifiable."

"this string" = const char* isn't correct. It allocates the block of memory to that fixed size, but it doesn't mean I cannot change the data to something else.

Yes I am reading the C++ standard, kind of what to work on c++ compiler. Clang's project is intriguing.
Last edited on Apr 5, 2011 at 10:25pm
Apr 5, 2011 at 10:32pm
String literal are not constant


Except...they are. See the part where the standard says:
An ordinary string literal has type “array of n const char


char *pSomeString = "This string is modifiable"; // the pointer is constant the string isn't.


Er, what? You are quite confused. The pointer is most definitely NOT constant.

Yes I am reading the C++ standard


Apparently you aren't reading it correctly then...re-read it again.
Apr 5, 2011 at 10:33pm
Well, I suppose that confirms you as a troll.
If you are not and this was just some spectacular lapse of judgment on your part, I would recommend to read the sections of the standard I posted, very very carefully. And then explain how it is consistent with any of what you said.

Or perhaps you were just confusing this with the following, similar syntax:
char str[]="Hello World!";

This is perfectly legal and you can change the characters around as much as you want.
The difference is that you're declaring an array here and initializing it with the character sequence specified. It is independent from the string literal.
This is different from a pointer, which will point to the beginning of the string literal itself if you initialize it this way.
Last edited on Apr 5, 2011 at 10:35pm
Apr 6, 2011 at 12:45am
Do not assume I am a troll.

I have multiple programming languages background. And I have been trying to understand the warnings I get from the modern compilers for stuff that has been compiling for twenty years.

This string class is part of a toy compiler which was written before the stl was part of C++. The standard string class wasn't available.

I apologize for sounding like a troll. I confuse c and assembly capabilities frequently. I realize the code above compiles like I expected to do. But in modern stuff it doesn't behave as expected. If I looked at the disassembly,modified it and re-hashed through an assembler the code would behave properly as expected.

Apr 6, 2011 at 12:46pm
@Azagaros: Think of this:

When you write a string literal like "Hello World" in your code, the compiler may make the actual pointer to the memory location to some special hardware memory which can by definition not be written to. If you would attempt a memory write operation on that hardware, the computer would just crash, because it is part of.. say.. some ROM memory block (which happens to contain "Hello World\0" as byte sequence).

The compiler is indeed allowed to do so, as he get the guarantee by the c++ - standard that the pointer points to a const memory block.

If you don't want to think of an such esoteric thing as ROM in your main memory, think of these "memory page write prevention - alert" systems deployed in current OS, where you can mark memory pages as read-only, and the operating system kills any process which attemps to write on these.

What happens in practice is probably much less interesting. Either nothing happens and it behaves as you think it should, or maybe the compiler may share the same memory block for strings with the same content. Or even crunch multiple strings into each other, say if you have "Hello World" in one place you may get a pointer to "World" in the other place which points into the memory block of the first data. In either case, if you would change the data behind the first literal, you may corrupt the application because you changed data behind any second pointer too. This behaviour may even change with some optimization options you set when compiling.

But the actual effect that happens in real life is not the point here. What the point is: Compiler behave weird if you violate their guarantees and by "weird" we mean "Works fine in all DEBUG-builds, all in-house presentations and tests but will crash utterly as soon as its installed at your customers computer" ;). You should never allow your programm to do anything that is labelled "undefined behaviour", except you REALLY know what the compiler is doing. Like in.. "you wrote that damn compiler". ;-)


Now, back to the topic.. The problem is, that good old C doesn't have any "const". But there are about a gazillion C-libraries out there, which have an interface taking "char*" pointers as parameter (since there was no const in C) but which do not change their parameter. These libraries have no problem with the mentioned read-only memory blocks, since they just never ever write on the memory in the first place.

The "normal" way of calling these functions from these libraries would be something like const_cast<char*>(my_const_char_pointer) (after you made sure by reading their specification, that they never modify the content of the string).

Since this is tedious for all string literals to do, the standard allowed a (discouraged) cast from string literals into char* pointer, but still - the standard never allowed for anyone to actually change memory beyond these pointers!

The warning just informs you of this use of deprecated and discouraged cast.


So bottom line: If you don't need to provide a constructor with char*, then don't provide one. Use "const char*" instead (or in your case, I would recommend using std::string anyway). You can call any const char* - function perfectly well with a char* too.


Or see it from this side: If you provide a function signature with "char*" in it, you say a message here. The message is: You need to pass a non-const character pointer here, because I will change your content. If I would not want to change your content, then I would take a "const char*" instead. (or the message could be "I am a stupid old C-interface and was written by people who could not make my pointer const. Please be assured that I will never change the parameter and imagine I would have had written 'const char*' if I could")
Last edited on Apr 6, 2011 at 12:50pm
Apr 6, 2011 at 4:23pm
I won't argue with you. I agree with all of what you said. The reason this class exists is some of the features I put with it that std::string doesn't have.

gcc is generating exception faults on const pointers on copy constructors of this class randomly, which worked on borland and watcom in release and debug mode. The exception fault is on a std library call on verifiable pointers. In other words I can print the data to the screen and its accurate. I can see the data in watches in the debugger. The code compiles. Its making me wonder to work around this I would have to write the actual function instead of the std function call, or wondering if I actually found a bug in gcc's code generation.

1
2
3
4
5
6
7
8
9
10
11
12
//class CString
//private:
//    char *m_pString;

CString::CString(const CString &cString)
{
    // copy constructor
    int newlen =strlen (cString.m_pString) +1; // the exception fault.
    m_pString = new char[newlen];
    strcpy (m_pString, cString.m_pString); // if it gets by the first one it exception faults.
}


On Gcc, the code works in one section of the program and doesn't in another.
Apr 6, 2011 at 5:13pm
Do you copy the memory block in your char* - constructor or only store the passed pointer? The former is probably what you want in almost all cases.

If the latter, maybe you are initializing the instance of the "cString" - parameter passed to your copy constructor somewhere with a memory block that got deleted afterwards?
Pages: 12