Why Modifying String Literals Causes Crashes in C

In the C programming language, attempting to alter a string literal triggers undefined behavior and frequently results in a runtime segmentation fault. This happens because compilers typically allocate string literals in read-only memory segments managed by the operating system's memory protection mechanisms. This article explores how string literals are represented in memory, why the C standard categorizes their modification as undefined behavior, the mechanism behind segmentation crashes, and how to properly initialize mutable character strings.

Memory Segments and the .rodata Section

When a C program is compiled into an executable binary (such as an ELF binary on Linux or a PE binary on Windows), the compiler and linker divide data into distinct memory segments based on usage:

  • Text Segment (.text): Holds the machine code instructions executed by the CPU.
  • Data Segment (.data): Holds initialized global and static variables that can be modified at runtime.
  • BSS Segment (.bss): Holds uninitialized global and static variables.
  • Read-Only Data Segment (.rodata or .rdata): Holds constants and string literals.

When you write a string literal like "Hello, World!", the compiler places its character sequence and terminating null byte (\0) into the .rodata segment. When the operating system loads the program into memory, it maps the pages containing the .rodata segment with read-only permissions via the Memory Management Unit (MMU).

The Hardware Trigger: How Segmentation Faults Occur

When a program executes an assignment to an address within a string literal:

char *str = "Hello";
str[0] = 'h'; /* Crash occurs here */

The CPU executes a write instruction targeting an address located in the read-only memory page. Because the MMU has flagged that page as read-only, the CPU detects an access permission violation and generates a hardware fault or exception.

The operating system kernel catches this hardware exception, determines that the process attempted to write to protected memory, and terminates the program by sending a signal—typically SIGSEGV (Signal 11, Segmentation Fault) on Unix-like systems, or throwing an EXCEPTION_ACCESS_VIOLATION on Windows.

The C Standard and Undefined Behavior

According to the ISO C standard (Section 6.4.5 in C11 and C17), the type of a string literal is an array of char. However, the standard explicitly states:

"If the program attempts to modify such an array, the behavior is undefined."

Because the behavior is undefined, the program is not guaranteed to crash. Potential outcomes include:

  • Immediate termination via a segmentation fault (the most common modern outcome).
  • Silent modification, if the environment or architecture does not enforce hardware memory protection (common in older systems or bare-metal microcontrollers without an MMU).
  • Corruption of other identical string literals in the program due to "string interning" (an optimization where the compiler shares a single memory location for duplicate literals).

Pointer vs. Array Declarations

Developers often confuse a pointer to a string literal with a character array initialized by a string literal.

1. Pointer to String Literal (Read-Only)

char *ptr = "Hello";

Here, "Hello" resides in the .rodata segment. The variable ptr is a pointer stored on the stack that simply holds the address of that read-only memory. Writing to ptr[0] attempts to modify .rodata, leading to a crash.

To prevent accidental modification at compile time, always qualify literal pointers with const:

const char *ptr = "Hello";
ptr[0] = 'h'; /* Compiler error: assignment of read-only location */

2. Character Array Initialization (Mutable)

char arr[] = "Hello";
arr[0] = 'h'; /* Safe and valid */

In this declaration, arr allocates space directly on the stack (or in .data if global) to hold 6 characters ('H', 'e', 'l', 'l', 'o', '\0'). The compiler copies the bytes from the read-only literal into this newly allocated stack memory during initialization. Because the array lives in writable stack memory, modifying its elements is entirely safe.