views:

225

answers:

6

Hello. I’ve got the following code:

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char* b = "bar";
    a = b;
    cout << a << ", " << b << endl;
    return 0;
}

This compiles and works, ie. prints bar, bar. Now I would like to demonstrate that what goes on here is not copying a string. I would like to change b and show that a also changes. I came up with this simple code:

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char* b = "bar";
    a = b;
    b[1] = 'u'; // ← just this line added
    cout << a << ", " << b << endl;
    return 0;
}

…but it segfaults. Why? The interesting thing is that the following modification runs just fine:

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char b[] = "bar"; // ← declaration changed here
    a = b;
    b[1] = 'u';
    cout << a << ", " << b << endl;
    return 0;
}

Why doesn’t it segfault like the previous one? I guess I am missing some important difference between the pointer-style and the array-style string initialization.

+8  A: 

You cannot change string constants, which is what you get when you use the pointer-to-literal syntax as in the first code samples.

See also this question: Is a string literal in c++ created in static memory?.

unwind
Also note that your code gives compiler warnings that explain this problem: "warning: deprecated conversion from string constant to ‘char*’"
schnaader
g++ -Wall -pedantic 4.01 did not, what do I turn on to get the warning?
zoul
Using g++ 4.3.2 here on Debian, parameters don't matter, simply calling "g++ test.cpp" is enough, adding -Wall, -pedantic or both didn't change anything. It seems this warning was added somewhere after 4.1/4.2.
schnaader
Thanks— err, thank You. (Character limit too strict for a simple Thanks :)
zoul
NB: Many older compilers and/or execution environments would let you get away with this (introducing some interesting possible bugs), so you see it from time to time.
dmckee
+6  A: 

When you write this:

char *b = "bar";

the compiler allocates an anonymous (nameless) memory area to store the string literal "bar". String literals may not be modified, so the compiler (with the help of the linker and operating system) puts the string literal in a part of the running program's memory space that is write-protected. When you try to modify it, the operating system catches it and causes your program to segmentation fault.

(Your code is C++, not C, but that's irrelevant to this question.)

Lars Wirzenius
A: 

This difference is perhaps compiler-specific. To demonstrate your point use malloc to allocate the buffer, then copy the string into this buffer and don't forget to use free when you no longer need the string.

sharptooth
+2  A: 

When you write:

char *foo = "bar";

What actually happens is that the "bar" is stored in the read-only segment of memory. Therefore, it is immutable. You get a segfault because you try to modify a read-only segment.

Gilad Naor
+2  A: 

You can also show that 'a' has been changed by printing the value of the pointer.

#include <iostream>
using namespace std;
int main()
{
    char* a = "foo";
    char* b = "bar";
    a = b;

    cout << (void*)a << ", " << (void*)b << endl;
}

This will print the address that 'a' and 'b' point at.
You have to cast to 'void*' because the operator << is overloaded for 'char*' to print out the string any other pointer will print the address.

Martin York
+1  A: 

In theory, a string literal should not be able to be assigned to a char*, only a 'const char*'. Then the compiler would stop you before you wrote seg faulting code.

Mr J