14

Consider the following code.

int main(void) {
    char * test = "abcdefghijklmnopqrstuvwxyz";
    test[5] = 'x';
    printf("%s\n", test);
    return EXIT_SUCCESS;
}

In my opinion, this should print abcdexghij. However, it just terminates without printing anything.

int main(void) {
    char * test = "abcdefghijklmnopqrstuvwxyz";
    printf("%s\n", test);
    return EXIT_SUCCESS;
}

This however, works just fine, so did I misunderstand the concept of manipulating C strings or something? In case it is important, I'm running Mac OS X 10.6 and it is a 32-bit binary I'm compiling.

5
  • 3
    I hate to say it, but this should really be in a C FAQ somewhere... it's been asked tens or hundreds of other times on Stack Overflow already.
    – ephemient
    Commented Sep 21, 2009 at 19:44
  • 1
    I'm sorry if this had been asked before, however I was unable to find an answer. I really did read the function reference and everything first, but I really didn't see what I was doing wrong. Could you point me to such a C FAQ?
    – fresskoma
    Commented Sep 21, 2009 at 20:58
  • 4
    @x3ro: Nobody has answered you about a C FAQ in 4 years?? The comp.lang.c FAQ is excellent. Section 8 covers characters and strings, and question 8.5 points to question 1.32, which addresses your specific question. Commented Sep 28, 2013 at 20:24
  • This is like doing int j = 5; and then trying to turn that 5 into a 6. You can change the value of j to 6, but you can't turn that 5 itself into a 6. You aren't trying to change the value of test here. You are trying to turn that string in your source code into something else! Commented Nov 15, 2017 at 0:20
  • @DavidSchwartz Actually that's not quite right. "abcdefghijklmnopqrstuvwxyz" is stored in memory somewhere, and that memory can potentially be modified. It's location can't be changed, that would be like changing number 5 in your example. But its content can't be modified in modern operating systems only, because compiler makes it so that operating system will prevent the string modification. It'd be undefined behavior in the standard anyway, but I'm sure some C compilers define it as legal. Related: stackoverflow.com/questions/56743877/…
    – hyde
    Commented Jun 20, 2022 at 9:54

5 Answers 5

36

Char pointers defined with an initialization value go into a read-only segment. To make them modifiable, you either need to create them on the heap (e.g. with new operator or malloc() function) or define them as an array.

Not modifiable:

char * foo = "abc";

Modifiable:

char foo[] = "abc";
1
  • 1
    foo[0] = 'x' still segfaults on my box
    – pm100
    Commented Feb 7, 2010 at 1:11
19

This answer is good, but not quite complete.

char * test = "abcdefghijklmnopqrstuvwxyz";

A string literal refers to an anonymous array object of type char[N] with static storage duration (meaning it exists for the entire execution of the program), where N is the length of the string plus one for the terminating '\0'. This object is not const, but any attempt to modify it has undefined behavior. (An implementation can make string literals writable if it chooses, but most modern compilers do not.)

The declaration above creates such an anonymous object of type char[27], and uses the address of that object's first element to initialize test. Thus an assignment like test[5] = 'x' attempts to modify the array, and has undefined behavior; typically it will crash your program. (The initialization uses the address because the literal is an expression of array type, which is implicitly converted in most contexts to a pointer to the array's first element.)

Note that in C++, string literals are actually const, and the above declaration would be illegal. In either C or C++, it's best to declare test as a pointer to const char:

const char *test = "abcdefghijklmnopqrstuvwxyz";

so the compiler will warn you if you attempt to modify the array via test.

(C string literals are not const for historical reasons. Before the 1989 ANSI C standard, the const keyword did not exist. Requiring it to be used in declarations like yours would have made for safer code, but it would have required existing code to be modified, something the ANSI committee tried to avoid. You should pretend that string literals are const, even though they aren't. If you happen to be using gcc, the -Wwrite-strings option will cause the compiler to treat string literals as const -- which makes gcc non-conforming.)

If you want to be able to modify the string that test refers to, you can define it like this:

char test[] = "abcdefghijklmnopqrstuvwxyz";

The compiler looks at the initializer to determine how big test needs to be. In this case, test will be of type char[27]. The string literal still refers to an anonymous mostly-read-only array object, but its value is copied into test. (A string literal in an initializer used to initialize an array object is one of the contexts in which an array does not "decay" to a pointer; the others are when it's the operand of unary & or sizeof.) Since there are no further references to the anonymous array, the compiler may optimize it away.

In this case, test itself is an array containing the 26 characters you specified, plus the '\0' terminator. That array's lifetime depends on where test is declared, which may or may not matter. For example, if you do this:

char *func(void) {
    char test[] = "abcdefghijklmnopqrstuvwxyz";
    return test; /* BAD IDEA */
}

the caller will receive a pointer to something that no longer exists. If you need to refer to the array outside the scope in which test is defined, you can define it as static, or you can allocate it using malloc:

char *test = malloc(27);
if (test == NULL) {
    /* error handling */
}
strcpy(test, "abcdefghijklmnopqrstuvwxyz";

so the array will continue to exist until you call free(). The non-standard strdup() function does this (it's defined by POSIX but not by ISO C).

Note carefully that test may be either a pointer or an array depending on how you declare it. If you pass test to a string function, or to any function that takes a char*, that doesn't matter, but something like sizeof test will behave very differently depending on whether test is a pointer or an array.

The comp.lang.c FAQ is excellent. Section 8 covers characters and strings, and question 8.5 points to question 1.32, which addresses your specific question. Section 6 covers the often confusing relationship between arrays and pointers.

0
4

String literals may not be modifiable; it's best to assume they aren't. See here for more details.

4

You should get into the habit of matching the type of the variable with the type of the initialiser. In this case:

const char* test = "abcdefghijklmnopqrstuvwxyz";

That way you will get a compiler error rather than a run-time error. Cranking your compiler warning level up to max may also help avoid such pitfalls. Why this is not an error in C is probably historical; early compilers allowed it and disallowing it might have broken too much existing code when the language was standardised. Now however operating systems do not allow it so it is academic.

0

Do:

 char * bar = strdup(foo);
 bar[5] = 'x';

strdup makes a modifiable copy.

And yes, you should really test that strdup didn't return NULL.

1
  • 2
    ...and free(bar) at the end if you use strdup()!
    – Drew Hall
    Commented Jul 26, 2010 at 22:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.