A value of type "const char *" cannot be used to initialize an entity of type "char *"

Hello Guys,

Following the stream completely on VS 2017 (everything, so not using any other tool). When Casey setting the filename to a char* I get this error message. Tried to find what is causing the issue, but the explanations not clear to me.

Can someone help me out please?

Edited by Zoltan Stomfai on Reason: Initial post
It seems the
1
char *fileName = (char *)__FILE__;

solved the issue.

Still not sure why do I need to cast it.

Edited by Zoltan Stomfai on
This happens because by default string literals (like "foobar") have const char* type. But because for Casey const correctness does not help, he ignores "const" keyword. And in C you cannot assign something with const type to non-const type, because it "potentially" lead to error. Like if you would try writing to fileName (fileName[0] = 'x') it would crash at runtime. Because compiler puts string literals into read only section of process.

To avoid this you need to either cast each string literal to char*, or you can instruct compiler to ignore warnings for casting string literals to non const type.
Though in either case trying to change the contents of the string literal will be undefined behavior. One of the reasons for const is to avoid such mistakes.
Consider:

1
2
char mystring[255];
char *a = mystring; 


This defines an array of 255 bytes (if char is an unsigned byte on your system) as "mystring" and assigns char *a to point to mystring[0] (first character in string).

Now consider the Pre-processor Macro of __FILE__, which is from "C and C++"

__FILE__ expands into a string literal, just as if you had written "/path/to/current/file.cpp"

A string literal is an unmodifiable "lvalue" -- in other words it is implicitly constant to write:
"this string"

This makes a lot of sense. There is no way at run-time a program should be able to modify the source code files, and since this was defined in the source code files and not in a way that makes sense to be modifiable, it is read only protected and therefore technically a "const char *" pointer to an unwrite-able block of memory (program constants) (This is quite well explained by this article on Stack Overflow: )

When you cast a const char * to a char *, this makes absolutely no sense. You are saying that your variable, let's call it foo, wants to allow writing to an un-write-able constant value, which is of the same type, but is allowed to be written.

Case in point:

1
2
const char *myconst = "this value";
char *foo = (char *) myconst;


You get away with this as long as you respect that the value cannot be written in your program's execution. Trying to modify the value of foo[0] ('t') would result in a segmentation fault.

The reason why VS2017 complains about this is because for the first time really in the history of Microsoft's Visual Studio, it complies most closely with ISO standards, which define C2440 as a blocking error, rather than a warning or being completely ignore-able as it may have in the past:

https://msdn.microsoft.com/en-us/library/dn449508.aspx (2013, 2015)
https://docs.microsoft.com/en-us/...ler-errors-1/compiler-error-c2440 (2017)

(There's also the compiler settings "Conformance Mode" and "C++ Language Standard", etc you might find while searching for this issue)

You might think you can turn it off by using the compiler option like this article states:
https://docs.microsoft.com/en-us/...le-string-literal-type-conversion (2017)

But, it doesn't seem to work for me, for perhaps a reason that is unique to my project properties (I have no idea). (I tried this with a default project for a Windows Console Application.)

My solution is to make a copy, or to simply use the proper "const char *" type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Example 1:

#include <string>
using namespace std;

string mine=string(__FILE__);

// Example 2:

const char *filename=__FILE__;

string user_entered_filename = whatever;
filename = user_entered_filename.c_str();
...


Both involve using std::string, one of the few STL objects I use. Of course, you wouldn't want to do this if you were going to write a DLL, in that case you'd need to write your own converter.

You can easily write your own string class, and use that instead of std::string, converting both char * and const char * to a common "string" that has a .c_str() equivalent method function, but all of this requires use of C++, not "pure C" -- you could write a function in C to do the conversion for you, too, using a static or global string warehouse.

You could also experiment with writing your own const_cast

Edited by Lost Astronaut Studios on
lost
When you cast a const char * to a char *, this makes absolutely no sense.

It does "not" make sense only for compiler. For CPU and you as a developer it makes absolute sense - in lowest level char* or const char* is just a pointer - 32-bit or 64-bit value, an address - which is the same for both things. If I won't be changing it then having const or no const keyword does not change a bit in compiled code.

But, it doesn't seem to work for me, for perhaps a reason that is unique to my project properties (I have no idea).
Not sure what this setting will help you. It is only about turning on extra warning. By default it is off. It is not about placing string literals in read-write data section.

My solution is to make a copy, or to simply use the proper "const char *" type.
There is much cheaper and simpler way than involving dynamic memory allocations - char array:

1
char modifiable_string[] = __FILE__;


You could also experiment with writing your own reinterpret_cast
You mean const_cast? const_cast is only "C++ cast" that allows to remove const'ness. reinterpret_cast will work only for changing same const'ness pointer/reference types. And anyways - even const_cast won't change anything. Its only about type checking in compiler, not about whether to place literal in read only on read-write data section.

Edited by Mārtiņš Možeiko on
There is much cheaper and simpler way than involving dynamic memory allocations - char array:

Not sure how much cheaper it really is, and for me, I like my way just fine. I tend to avoid raw arrays anyway, since I've grown out of using low-level driver languages like C for building applications, and I'm not afraid to get my hands dirty with something higher level. Besides, my other suggestion is to simply use a const char * which matches the original type.

Still, no one has explained why/how Casey doesn't have to cast.

Edited by Lost Astronaut Studios on
I think it's pretty obvious why it is cheaper in this situation:
1) it does not involve potential syscall - that means performance is not affected by potential context switch, swapping to slow swap file (in case of hdd) or other unpredictable thread scheduling things from kernel. Deterministic performance matters a lot for game development.
2) it will never fail - meaning no potential exceptions, or abnormal program terminations, or extra code for you to write to handle these situations
3) less code generated - meaning better performance, in case code is evicted from cache, or branch predictions and other things

Its ok to like your own way. But it is not ok to push your way as right one without explaining why it is better and how it works, including disadvantages - which Handmade Network/Hero is all about.

Still, no one has explained why/how Casey doesn't have to cast.
That's because he does not use /Zc:strictStrings argument which is turned on by default VS2017 project (via /permissive- argument). His way of compiling on command line allows to specify exactly what you want to use, instead of relying what Microsoft thinks you need.

Edited by Mārtiņš Možeiko on
This reply is contradicting what you said earlier. You jumped me for posting articles about those settings, by telling me they were irrelevant, and now you're telling me they are relevant, even though they don't work.

Using a command line versus modifying the command line settings in a Project is pretty much the same thing.
I said that /Zc:strictStrings setting is irrelevant for making literal writeable. Because I thought that is what you were talking about. Quote - "...makes sense to be modifiable, it is read only protected..." Sorry if I misunderstood it. This setting only controls warning about type casting. That is why there are no warnings for Casey.

Just to repeat myself - this setting won't change location of segment where actual value is placed (.rdata vs .data). Casey doesn't want to modify string literals, he just doesn't want to see these warnings or waste his time with dealing with const vs non-const types.

Using a command line versus modifying the command line settings in a Project is pretty much the same thing.
Yes. And? MS defaults its not what Casey is using. He uses things that he neededs, and not what VS2017 default project is generating.

Edited by Mārtiņš Možeiko on