String literals represent null-terminated, static-duration arrays of char. Because they have static storage duration, a string literal or a pointer to the same underlying array can safely be used in several ways that a pointer to an automatic array cannot. For example, returning a string literal from a function has well-defined behavior:
const char *get_hello() {
return "Hello, World!"; /* safe */
}
For historical reasons, the elements of the array corresponding to a string literal are not formally const. Nevertheless, any attempt to modify them has undefined behavior. Typically, a program that attempts to modify the array corresponding to a string literal will crash or otherwise malfunction.
char *foo = "hello";
foo[0] = 'y'; /* Undefined behavior - BAD! */
Where a pointer points to a string literal -- or where it sometimes may do -- it is advisable to declare that pointer's referent const to avoid engaging such undefined behavior accidentally.
const char *foo = "hello";
/* GOOD: can't modify the string pointed to by foo */
On the other hand, a pointer to or into the underlying array of a string literal is not itself inherently special; its value can freely be modified to point to something else:
char *foo = "hello";
foo = "World!"; /* OK - we're just changing what foo points to */
Furthermore, although initializers for char arrays can have the same form as string literals, use of such an initializer does not confer the characteristics of a string literal on the initialized array. The initializer simply designates the length and initial contents of the array. In particular, the elements are modifiable if not explicitly declared const:
char foo[] = "hello";
foo[0] = 'y'; /* OK! */