Why do I get a segmentation fault when writing to a "char *s" initialized with a string literal, but not "char s[]"?

Viewed 93350

The following code receives seg fault on line 2:

char *str = "string";
str[0] = 'z';  // could be also written as *str = 'z'
printf("%s\n", str);

While this works perfectly well:

char str[] = "string";
str[0] = 'z';
printf("%s\n", str);

Tested with MSVC and GCC.

19 Answers

See the C FAQ, Question 1.32

Q: What is the difference between these initializations?
char a[] = "string literal";
char *p = "string literal";
My program crashes if I try to assign a new value to p[i].

A: A string literal (the formal term for a double-quoted string in C source) can be used in two slightly different ways:

  1. As the initializer for an array of char, as in the declaration of char a[] , it specifies the initial values of the characters in that array (and, if necessary, its size).
  2. Anywhere else, it turns into an unnamed, static array of characters, and this unnamed array may be stored in read-only memory, and which therefore cannot necessarily be modified. In an expression context, the array is converted at once to a pointer, as usual (see section 6), so the second declaration initializes p to point to the unnamed array's first element.

Some compilers have a switch controlling whether string literals are writable or not (for compiling old code), and some may have options to cause string literals to be formally treated as arrays of const char (for better error catching).

Normally, string literals are stored in read-only memory when the program is run. This is to prevent you from accidentally changing a string constant. In your first example, "string" is stored in read-only memory and *str points to the first character. The segfault happens when you try to change the first character to 'z'.

In the second example, the string "string" is copied by the compiler from its read-only home to the str[] array. Then changing the first character is permitted. You can check this by printing the address of each:

printf("%p", str);

Also, printing the size of str in the second example will show you that the compiler has allocated 7 bytes for it:

printf("%d", sizeof(str));

Most of these answers are correct, but just to add a little more clarity...

The "read only memory" that people are referring to is the text segment in ASM terms. It's the same place in memory where the instructions are loaded. This is read-only for obvious reasons like security. When you create a char* initialized to a string, the string data is compiled into the text segment and the program initializes the pointer to point into the text segment. So if you try to change it, kaboom. Segfault.

When written as an array, the compiler places the initialized string data in the data segment instead, which is the same place that your global variables and such live. This memory is mutable, since there are no instructions in the data segment. This time when the compiler initializes the character array (which is still just a char*) it's pointing into the data segment rather than the text segment, which you can safely alter at run-time.

In the first code, "string" is a string constant, and string constants should never be modified because they are often placed into read only memory. "str" is a pointer being used to modify the constant.

In the second code, "string" is an array initializer, sort of short hand for

char str[7] =  { 's', 't', 'r', 'i', 'n', 'g', '\0' };

"str" is an array allocated on the stack and can be modified freely.

Because the type of "whatever" in the context of the 1st example is const char * (even if you assign it to a non-const char*), which means you shouldn't try and write to it.

The compiler has enforced this by putting the string in a read-only part of memory, hence writing to it generates a segfault.

char *str = "string";  

The above sets str to point to the literal value "string" which is hard-coded in the program's binary image, which is probably flagged as read-only in memory.

So str[0]= is attempting to write to the read-only code of the application. I would guess this is probably compiler dependent though.

char *str = "string";

allocates a pointer to a string literal, which the compiler is putting in a non-modifiable part of your executable;

char str[] = "string";

allocates and initializes a local array which is modifiable

The C FAQ that @matli linked to mentions it, but no one else here has yet, so for clarification: if a string literal (double-quoted string in your source) is used anywhere other than to initialize a character array (ie: @Mark's second example, which works correctly), that string is stored by the compiler in a special static string table, which is akin to creating a global static variable (read-only, of course) that is essentially anonymous (has no variable "name"). The read-only part is the important part, and is why the @Mark's first code example segfaults.

The

 char *str = "string";

line defines a pointer and points it to a literal string. The literal string is not writable so when you do:

  str[0] = 'z';

you get a seg fault. On some platforms, the literal might be in writable memory so you won't see a segfault, but it's invalid code (resulting in undefined behavior) regardless.

The line:

char str[] = "string";

allocates an array of characters and copies the literal string into that array, which is fully writable, so the subsequent update is no problem.

String literals like "string" are probably allocated in your executable's address space as read-only data (give or take your compiler). When you go to touch it, it freaks out that you're in its bathing suit area and lets you know with a seg fault.

In your first example, you're getting a pointer to that const data. In your second example, you're initializing an array of 7 characters with a copy of the const data.

In the first place, str is a pointer that points at "string". The compiler is allowed to put string literals in places in memory that you cannot write to, but can only read. (This really should have triggered a warning, since you're assigning a const char * to a char *. Did you have warnings disabled, or did you just ignore them?)

In the second place, you're creating an array, which is memory that you've got full access to, and initializing it with "string". You're creating a char[7] (six for the letters, one for the terminating '\0'), and you do whatever you like with it.

Assume the strings are,

char a[] = "string literal copied to stack";
char *p  = "string literal referenced by p";

In the first case, the literal is to be copied when 'a' comes into scope. Here 'a' is an array defined on stack. It means the string will be created on the stack and its data is copied from code (text) memory, which is typically read-only (this is implementation specific, a compiler can place this read-only program data in read-writable memory also).

In the second case, p is a pointer defined on stack (local scope) and referring a string literal (program data or text) stored else where. Usually modifying such memory is not good practice nor encouraged.

Constant memory

Since string literals are read-only by design, they are stored in the Constant part of memory. Data stored there is immutable, i.e., cannot be changed. Thus, all string literals defined in C code get a read-only memory address here.

Stack memory

The Stack part of memory is where the addresses of local variables live, e.g., variables defined in functions.


As @matli's answer suggests, there are two ways of working with string these constant strings.

1. Pointer to string literal

When we define a pointer to a string literal, we are creating a pointer variable living in Stack memory. It points to the read-only address where the underlying string literal resides.

#include <stdio.h>

int main(void) {
  char *s = "hello";
  printf("%p\n", &s);  // Prints a read-only address, e.g. 0x7ffc8e224620
  return 0;
}

If we try to modify s by inserting

s[0] = 'H';

we get a Segmentation fault (core dumped). We are trying to access memory that we shouldn't access. We are attempting to modify the value of a read-only address, 0x7ffc8e224620.

2. Array of chars

For the sake of the example, suppose the string literal "Hello" stored in constant memory has a read-only memory address identical to the one above, 0x7ffc8e224620.

#include <stdio.h>

int main(void) {
  // We create an array from a string literal with address 0x7ffc8e224620.
  // C initializes an array variable in the stack, let's give it address
  // 0x7ffc7a9a9db2.
  // C then copies the read-only value from 0x7ffc8e224620 into 
  // 0x7ffc7a9a9db2 to give us a local copy we can mutate.
  char a[] = "hello";

  // We can now mutate the local copy
  a[0] = 'H';

  printf("%p\n", &a);  // Prints the Stack address, e.g. 0x7ffc7a9a9db2
  printf("%s\n", a);   // Prints "Hello"

  return 0;
}

Note: When using pointers to string literals as in 1., best practice is to use the const keyword, like const *s = "hello". This is more readable and the compiler will provide better help when it's violated. It will then throw an error like error: assignment of read-only location ā€˜*s’ instead of the seg fault. Linters in editors will also likely pick up the error before you manually compile the code.

Section 5.5 Character Pointers and Functions of K&R also discusses about this topic:

There is an important difference between these definitions:

char amessage[] = "now is the time"; /* an array */
char *pmessage = "now is the time"; /* a pointer */

amessage is an array, just big enough to hold the sequence of characters and '\0' that initializes it. Individual characters within the array may be changed but amessage will always refer to the same storage. On the other hand, pmessage is a pointer, initialized to point to a string constant; the pointer may subsequently be modified to point elsewhere, but the result is undefined if you try to modify the string contents.

Related