Question
In C, a string literal can be used in a declaration in two common ways:
char s[] = "hello";
or:
char *s = "hello";
What is the difference between these two declarations?
I want to understand what happens in terms of memory layout, mutability, and storage duration, both at compile time and at run time.
Short Answer
By the end of this page, you will understand why char s[] = "hello"; creates an array containing a copy of the string, while char *s = "hello"; creates a pointer to a string literal. You will also learn how their memory is stored, how they behave differently when modified, and why this distinction matters in real C programs.
Concept
In C, a string literal like "hello" is an array of characters stored by the implementation, with a terminating null character \0 added automatically.
These two declarations look similar, but they create very different objects:
char s[] = "hello";
char *s = "hello";
char s[] = "hello";
This declares s as an array of 6 characters:
{'h', 'e', 'l', 'l', 'o', '\0'}
The contents of the string literal are copied into the array when s is initialized. After that, s is its own array object.
shas storage for 6charvalues.- The array is modifiable if it is not declared
const. sizeof(s)gives the full array size, which is6.
char *s = "hello";
This declares s as a pointer to char. The pointer is initialized to point at the first character of the string literal.
sitself is a pointer variable.- The string literal exists elsewhere in memory.
- The literal is not safely modifiable.
sizeof(s)gives the size of the pointer, not the string length.
Although older C code often uses char *s = "hello";, attempting to modify the literal through that pointer causes undefined behavior.
Why this matters
This difference affects:
- whether the characters can be changed
- what memory is allocated
- how
sizeofbehaves - whether reassignment is allowed
- whether the data belongs to your variable or is shared read-only literal data
Understanding this is essential for string handling, function parameters, memory safety, and debugging in C.
Mental Model
Think of these two forms like this:
char s[] = "hello";means make me my own writable notepad and copyhelloonto it.char *s = "hello";means give me the address of a printed sign that already sayshello.
With the notepad, you can erase and rewrite letters:
s[0] = 'H';
With the sign, you are only pointing at something that already exists somewhere else. Trying to rewrite the sign is not allowed safely.
Also:
- an array is the actual storage
- a pointer is only an address
That single idea explains most of the behavior difference.
Syntax and Examples
Core syntax
char s[] = "hello";
char *p = "hello";
Example 1: Array initialized from a string literal
#include <stdio.h>
int main(void) {
char s[] = "hello";
s[0] = 'H';
printf("%s\n", s);
return 0;
}
Output:
Hello
Explanation:
sis an array with its own storage.- The characters from
"hello"are copied into it. - Changing
s[0]is allowed.
Example 2: Pointer to a string literal
#include <stdio.h>
int main(void) {
char *p = "hello";
printf("%s\n", p);
return 0;
}
Explanation:
ppoints to the first character of the string literal.- The pointer is valid for reading the string.
- You should not modify the literal through
p.
Example 3: Reassignment difference
#include <stdio.h>
int main(void) {
char s[] = "hello";
char *p = "hello";
p = "world"; // allowed: pointer now points somewhere else
// s = "world"; // error: arrays cannot be assigned
printf("%s\n", s);
printf("%s\n", p);
return 0;
}
Explanation:
- A pointer variable can be changed to point to another string.
- An array name is not assignable.
Example 4: sizeof difference
#include <stdio.h>
int main(void) {
char s[] = "hello";
char *p = "hello";
printf("sizeof(s) = %zu\n", sizeof(s));
printf("sizeof(p) = %zu\n", sizeof(p));
return 0;
}
Typical output on a 64-bit system:
sizeof(s) = 6
sizeof(p) = 8
Explanation:
sizeof(s)is the size of the whole array.sizeof(p)is the size of a pointer.- To get the string length, use
strlen, notsizeofon a pointer.
Step by Step Execution
Consider this code:
#include <stdio.h>
int main(void) {
char s[] = "hello";
char *p = "hello";
printf("%c %c\n", s[1], p[1]);
return 0;
}
Step-by-step
- The compiler sees the string literal
"hello". - That literal represents the character sequence:
'h', 'e', 'l', 'l', 'o', '\0'
-
For
char s[] = "hello";- space for 6 characters is created for
s - those 6 characters are copied into
s
Memory idea:
- space for 6 characters is created for
s: [h][e][l][l][o][\0]
-
For
char *p = "hello";- a pointer variable
pis created - it stores the address of the first character of the string literal
Memory idea:
- a pointer variable
p ----> [h][e][l][l][o][\0]
s[1]reads the second element of the arrays, which is'e'.p[1]reads the second character from the memoryppoints to, which is also'e'.- The program prints:
e e
Important detail
Even though both can be used similarly for reading characters, the underlying storage is different:
sowns its character dataponly refers to character data stored elsewhere
Real World Use Cases
When char s[] = "hello"; is useful
Editable local strings
char filename[] = "report.txt";
filename[0] = 'R';
Use this when you want to modify the characters.
Fixed-size local buffers initialized with text
char status[] = "pending";
This is common when a program starts with default text and later updates it.
When char *s = "hello"; is useful
Referring to constant message text
char *message = "File not found";
This is useful when you only need to read or print the text.
Selecting one of several literals
char *level;
if (error) {
level = "error";
} else {
level = "ok";
}
The pointer can be redirected to different string literals easily.
Returning or passing read-only text
In real code, this is usually better written as:
const char *message = "ready";
That makes the non-modifiable intent explicit.
In APIs and data processing
- log messages often point to string literals
- command names may be stored as pointers to literals
- editable input buffers are usually arrays
- parsed or copied text often lives in writable arrays or dynamically allocated memory
Real Codebase Usage
In real projects, developers usually make the distinction more explicit.
Common pattern: use const char * for literals
const char *message = "hello";
This communicates:
- the text should not be modified
- the variable is just a pointer to existing character data
Common pattern: arrays for writable buffers
char buffer[64] = "hello";
This is used when code will:
- append text
- replace characters
- tokenize the string
- pass it to functions that modify the buffer
Validation and defensive coding
When a function should only read a string:
void print_name(const char *name) {
if (name == NULL) {
return;
}
printf("%s\n", name);
}
This is a common pattern in production C code:
const char *for read-only input- guard clause for
NULL
Configuration and lookup tables
Developers often store pointers to string literals in arrays:
const char *modes[] = {"read", "write", "append"};
This avoids copying string data repeatedly.
Error handling messages
const char *get_error_message(int code) {
switch (code) {
case 1: return "Invalid input";
case 2: return "Out of memory";
default: return "Unknown error";
}
}
These returned values are pointers to string literals, which is efficient and common.
Common Mistakes
1. Trying to modify a string literal through a pointer
Broken code:
char *s = "hello";
s[0] = 'H';
Why this is wrong:
- the pointer refers to a string literal
- modifying it causes undefined behavior
Safer version:
char s[] = "hello";
s[0] = 'H';
Or if it should be read-only:
const char *s = "hello";
2. Assuming an array and a pointer are the same thing
Broken thinking:
- both work with
%s - both use
[] - therefore they are identical
They are not identical:
- an array stores elements directly
- a pointer stores an address
3. Using sizeof on a pointer to get string length
Broken code:
char *s = "hello";
printf("%zu\n", sizeof(s));
This prints pointer size, not string length.
Correct approach:
#include <string.h>
printf("%zu\n", strlen(s));
4. Trying to assign to an array after declaration
Broken code:
char s[] = "hello";
s = "world";
Why this fails:
- arrays are not assignable in C
Correct approach:
char *p = "hello";
p = "world";
Or copy into an existing writable array:
#include <string.h>
char s[16] = "hello";
strcpy(s, "world");
5. Forgetting the null terminator in manual arrays
Broken code:
char s[5] = {'h', 'e', 'l', 'l', 'o'};
printf("%s\n", s);
Why this is dangerous:
%sexpects a null-terminated string- this array is not a proper C string
Correct version:
char s[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
Or simply:
char s[] = "hello";
Comparisons
| Concept | char s[] = "hello"; | char *s = "hello"; |
|---|---|---|
What s is | Array of char | Pointer to char |
| Memory ownership | Own storage for characters | Stores address only |
| Initial data | Copy of literal placed in array | Points to literal |
| Modifiable characters | Yes | No, not safely |
Reassign s | No | Yes |
sizeof(s) | Size of entire array | Size of pointer |
| Typical use | Writable string buffer | Reference to read-only text |
Related comparison: char * vs const char *
| Declaration | Meaning | Safe to modify through pointer? |
|---|---|---|
char *s = "hello"; | Pointer to literal, but type does not express read-only intent clearly | No |
const char *s = "hello"; | Pointer to read-only text | No |
In modern C style, const char * is usually preferred for string literals.
Related comparison: array vs pointer parameter
When passed to a function, an array usually decays to a pointer:
void print_text(const char *s) {
printf("%s\n", s);
}
So both of these work:
char a[] = "hello";
const char *p = "hello";
print_text(a);
print_text(p);
But outside function calls, arrays and pointers remain different types.
Cheat Sheet
char s[] = "hello"; // array containing a copy: {'h','e','l','l','o','\0'}
char *p = "hello"; // pointer to string literal
const char *q = "hello"; // preferred for read-only literal
- String literals are null-terminated.
char s[] = "hello";creates writable array storage.char *p = "hello";creates a pointer to literal storage.- Modifying a string literal is undefined behavior.
- Arrays are not assignable.
- Pointers are assignable.
sizeof(array)gives total bytes in the array.sizeof(pointer)gives pointer size.strlen(string)gives character count before\0.
Quick rules:
- Want to edit the text? Use an array.
- Want to point at fixed text? Use
const char *. - Need a function to read a string? Usually use
const char *.
Example:
char name[] = "alice";
name[0] = 'A';
const char *msg = "done";
// msg[0] = 'D'; // not allowed
FAQ
Is char s[] = "hello"; the same as char *s = "hello";?
No. The first creates an array containing the characters. The second creates a pointer to a string literal.
Can I modify char *s = "hello";?
You should not. Modifying the string literal through that pointer causes undefined behavior.
Why does sizeof give different results?
Because in one case s is an array, and in the other case s is a pointer. Arrays and pointers are different types.
Where is the string literal stored?
The C standard does not require a specific memory section, but implementations typically place string literals in static storage, often in read-only memory.
Does char s[] = "hello"; copy the string?
Yes. The array is initialized with the literal's characters, including the terminating \0.
Why do many examples use const char * instead of char *?
Because string literals should be treated as read-only. const char * makes that intent clear and helps prevent mistakes.
Can two pointers to the same literal share storage?
Yes. Compilers may store identical string literals in the same location, although you should not write code that depends on whether they do.
What happens when I pass char s[] to a function?
In most expressions, including function arguments, the array decays to a pointer to its first element.
Mini Project
Description
Build a small C program that demonstrates the practical difference between a writable character array and a pointer to a string literal. This helps you see memory behavior, modification rules, and reassignment in one place.
Goal
Create a program that safely modifies an array-based string, reassigns a pointer-based string, and prints the differences using sizeof and output.
Requirements
- Declare one string using
char name[] = "hello"; - Declare one string using
const char *message = "hello"; - Modify the first character of the array-based string
- Reassign the pointer-based string to a different literal
- Print both values and print their
sizeofresults
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.