views:

619

answers:

6

This code behaves weird in MS Visual Studio:

char *s = "hello";
s[0] = 'a';
printf(s);

In release build with optimization turned on it ignores s[0] = 'a' and prints "hello". Without optimization or in debug build it crashes with access violation.
Is this behavior is c++ standard compliant or no? In my opinion, compiler should only allow constant references to string literals, i.e.

const char *s = "hello";

EDIT: I know why it works like this, I do not understand why I am allowed to make non const reference to read only memory.

+3  A: 

I think C++ compilers are allowed to allocate string literals in read only memory pages per the standard.

Mehrdad Afshari
+12  A: 

No, it's not a bug in the compiler. When you write:

char* s = "hello";

The string constant "hello" will be placed in a read-only section and should generate an exception if you try to modify it. (an OS exception, not a C++ exception).

To make it writable, you have to either use an array:

char s[] = { 'h', 'e', 'l', 'l', 'o', 0 };

or, if you really need a pointer, make it point to an array:

char _s[] = { 'h', 'e', 'l', 'l', 'o', 0 };
char* s = _s;

I can see your point about only allowing const pointers to be initialized with string literals, but I think that would break a lot of existing code.

Ferruccio
It's enough to write `char s[] = "Hallo";`, no need to spell the string literal out as an array!
Konrad Rudolph
I wonder how this is defined in the spec.
shoosh
In fact, for a string of N characters: both are valid i.e.char s[ N ] = "12...N" and char s[ N + 1 ] = "12...N";
dirkgently
@Konrad - That's true. I had forgotten you could do that.
Ferruccio
+2  A: 

This won't work because *s is pointing to the memory address of a string constant, which you're not allowed to change.

I'm actually a little surprised you don't don't get an access violation when it's compiled with optimizations.

Dana
I think that optimizer simplifies printf(s) to printf("hello"), since s points to read only memory. Then optimizer throws away s[0] = 'a' as unnecessary operation, since it does not affect anything.
Pavel Dogurevich
Ahh, interesting!
Dana
A: 

As the others said, you can't modify a string literal. I also wonder why you don't get a compiler warning in your code. The compiler can for sure figure out that you're trying to write to read only memory.

Nils Pipenbrinck
A: 
char *s = "foo";

is a tricky pony. It doesn't tell you that this really is a read-only string when in reality it is. This is so, because, you could have your colleague write another string like:

char *t = "foo";

Now, the compiler, at its helpful best will keep only one copy, and changing one would mean a lot of work only to keep you and your friend happy. So, it doesn't try to do that. This is something you should find in the standard. Guess what, what you're doing invokes UB.

That said, if you were to have your way, it would break a lot of legacy code which the poor guys at the Standards Committee couldn't afford. So, there you are.

Remember the const we are guilty of carefree usage wasn't born in a day. Bjarne did a lot of soul-searching and so did a lot of others whether to put it in or not. In fact, he had this excellent idea of having read-only and write-only variables ... but I'll save that story for another day.

And finally, there is our good friend C, who we need to take care of. So ...

dirkgently
+7  A: 

The reason why this code is allowed in the first place (rather than requiring the declaration to be of type char const*) is backwards compatibility to old C code.

Most modern compilers in strict mode will issue a warning for the above code, though!

Konrad Rudolph
I have used Visual Studio 2008 express with maximum warning level. It shows no warnings :(
Pavel Dogurevich
@zuranthus - compilers *might* issue warnings, but are not required to. unfortunately, assigning const string data to non-const variables is so common (which is why it was permitted int he first place), warnings are likely to be overwhelming in any decent-sized body of code.
Michael Burr
@zuranthus - unfortunately the "maximum" level (/W4) does not show all warnings. If you haven't already, try it with /Wall.
Ferruccio
@Ferruccio - no, still nothing, even with /Wall.
Pavel Dogurevich
this is what i don't like about msvc. it mixes together C and C++ and its extensions together.and one does never know when one compiles in strict or non-strict mode.you have to enable W4 for example just to be warned when you do string (which is invalid c++) i read in an msdn belog.
Johannes Schaub - litb