This is one of the most misunderstood topics in C. It directly affects how you should use string functions like strcpy and strcmp, which behave differently depending on whether you pass a literal or an array. char *str = "hello" and char str[] = "hello" both give you access to the string “hello” — but they are stored differently, have different mutability rules, and behave differently with sizeof. Confusing them causes crashes.
Where Each One Lives in Memory
char* — pointer to a string literal
char *str = "hello";
The string "hello" is stored in the read-only data segment of your program — the same region that stores compiled constants. str is a pointer variable on the stack that holds the address of that read-only memory.
Memory layout:
Stack Read-only segment
┌──────────┐ ┌─────────────────────────┐
│ str │──────────▶ │ 'h' 'e' 'l' 'l' 'o' │
└──────────┘ └─────────────────────────┘
char[] — array on the stack
char str[] = "hello";
The compiler copies the characters from the string literal into a stack-allocated array at runtime. There is no separate read-only string — the data lives on the stack and is fully writable.
Memory layout:
Stack
┌─────────────────────────┐
│ 'h' 'e' 'l' 'l' 'o' │ ← str (the array itself)
└─────────────────────────┘
Mutability — The Critical Difference
#include <stdio.h>
int main() {
char *ptr = "hello";
ptr[0] = 'H'; /* CRASH — writing to read-only memory */
char arr[] = "hello";
arr[0] = 'H'; /* fine — writing to the stack copy */
printf("%sn", arr); /* prints: Hello */
return 0;
}
Attempting to modify a string literal through a char* is undefined behaviour. On most modern systems it produces a segmentation fault because the read-only segment is marked non-writable by the OS.
GCC will not warn you about this by default. Always compile with -Wwrite-strings to catch it at compile time:
gcc -Wwrite-strings -o program program.c
/* warning: assignment discards 'const' qualifier from pointer target type */
sizeof Behaves Differently
#include <stdio.h>
int main() {
char *ptr = "hello";
char arr[] = "hello";
printf("sizeof(ptr) = %zun", sizeof(ptr)); /* 8 (pointer size on 64-bit) */
printf("sizeof(arr) = %zun", sizeof(arr)); /* 6 (5 chars + null terminator) */
return 0;
}
sizeof(ptr) gives you the size of the pointer itself (8 bytes on a 64-bit system), not the length of the string. sizeof(arr) gives you the size of the entire array including the null terminator.
This matters when you try to copy strings:
/* WRONG — copies 8 bytes (pointer size), not 6 */
memcpy(dest, ptr, sizeof(ptr));
/* CORRECT */
memcpy(dest, ptr, strlen(ptr) + 1); /* +1 for null terminator */
/* CORRECT for arrays */
memcpy(dest, arr, sizeof(arr)); /* sizeof gives the right size */
What Happens When You Pass Them to Functions
This is where arrays and pointers converge — and where confusion deepens.
#include <stdio.h>
void print_size(char *s) {
printf("Inside function: %zun", sizeof(s)); /* always 8 — pointer size */
}
int main() {
char arr[] = "hello";
printf("In main: %zun", sizeof(arr)); /* 6 — correct */
print_size(arr); /* arr decays to a pointer here */
return 0;
}
When you pass an array to a function, it decays to a pointer to its first element. The function receives a char*, not a char[]. Inside the function, sizeof gives the pointer size, not the array size. This is why C functions that operate on strings also take a length parameter.
Can You Reassign Them?
char *ptr = "hello";
ptr = "world"; /* fine — you are just changing where ptr points */
char arr[] = "hello";
arr = "world"; /* ERROR — arrays are not assignable */
A pointer is a variable that holds an address. You can change it to point somewhere else. An array name is a constant address — you cannot make it refer to a different memory location.
The const-correct Way to Declare String Literals
/* This is technically correct */
const char *str = "hello";
str[0] = 'H'; /* compile-time error — const prevents this */
When you declare a pointer to a string literal, it should be const char*. This tells both the compiler and future readers that the string is not to be modified. Most C codebases do not enforce this (C allows the non-const assignment for historical reasons), but it is the right way to write it.
TL;DR
char *str = "hello"— pointer to read-only memory. Do not modify the string.char str[] = "hello"— writable stack copy. Modification is safe.sizeof(char*)gives pointer size (8 bytes).sizeof(char[])gives array size.- Arrays decay to pointers when passed to functions — size information is lost.
- Use
const char*when you will not modify the string. - Compile with
-Wwrite-stringsto catch accidental string literal writes.