Char Array vs String Literal in C – Which One Can You Modify?

Writing into a string: sometimes it works perfectly, sometimes it crashes on the spot — and the difference is a single character in the declaration. This question from our C Programming Quiz App is the definitive test of whether you know what char s[] = "hello" actually creates.

The Quiz Question

char s[] = "hello";
s[1] = 'a';
printf("%s", s);

What is printed by this code?

  1. hello
  2. hallo
  3. Compile error
  4. Undefined behavior

The Correct Answer: hallo

char s[] = "hello" declares an array and copies the literal’s characters into it. The array is ordinary writable memory on the stack — modifying it is completely legal. s[1] = 'a' replaces the 'e', and the string prints as hallo. Verified on gcc 13.3 and Apple clang 21, clean under -ansi -Wall -Wextra:

$ gcc -ansi -Wall -Wextra modify.c && ./a.out
hallo

Why Each Wrong Answer Is Wrong

Why not “hello”?

The assignment is real and takes effect immediately — there’s no copy-on-write or silent failure in C. By the time printf runs, index 1 holds 'a'.

Why not a compile error?

s is a plain char array and s[1] is a plain modifiable char. Nothing here is const. Both compilers accept it without a whisper, even with all warnings on.

Why not undefined behavior?

This is the option that catches experienced programmers, because a nearly identical program is UB — the pointer version:

char *s = "hello";   /* pointer to the literal itself */
s[1] = 'a';          /* undefined behavior! */

We ran it on both platforms:

$ ./a.out        (gcc 13.3, Linux)
Segmentation fault (core dumped)

$ ./a.out        (Apple clang 21, macOS)
Bus error: 10

Same tokens except * instead of [] — and it dies instantly. The quiz code is the array version, which owns a writable copy. Knowing which side of that line you’re on is the whole question.

Array Copy vs Pointer to Literal

The two declarations differ in where the characters live:

char s[] = "hello";   →  [h][a][l][l][o][\0]  your own stack copy — writable
char *p  = "hello";   →  p ──→ literal in read-only storage — look, don't touch

String literals are typically placed in a read-only segment (.rodata), and the OS enforces it with page protection — hence the segfault. The array form runs the literal through an initializer instead: the compiler copies the six bytes into stack memory that belongs to s. That copy costs a few bytes and an instruction or two, which is exactly the price of being allowed to edit it. If you only need to read the string, declare the pointer const char *p = "hello"; and the compiler will turn the crash into a compile-time error — the char pointer question covers that side of the story.

Frequently Asked Questions

Can you modify a string in C?

You can modify a char array initialized from a literal (char s[] = "hello") — it’s your own writable copy. You must never modify what a pointer to a string literal refers to (char *s = "hello") — that’s undefined behavior and typically crashes.

What’s the difference between char s[] and char *s?

char s[] creates an array holding a copy of the literal’s characters; char *s creates a pointer aimed at the literal itself, which usually lives in read-only memory. Writes through the array are legal; writes through the pointer are undefined behavior.

Why does modifying a string literal crash?

Compilers place literals in a read-only data segment and the OS marks those pages non-writable. A write triggers a hardware protection fault — reported as a segmentation fault on Linux or a bus error on macOS.

Related Reading

Recommended Books

This question is #46 in the C Programming Quiz App — 155 questions with explanations covering arrays, strings, pointers, and more.
Download on Google Play →

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>