Question

Difference Between `char s[]` and `char *s` in C

cstringcharconstants

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.

  • s has storage for 6 char values.
  • The array is modifiable if it is not declared const.
  • sizeof(s) gives the full array size, which is 6.

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.

  • s itself 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 sizeof behaves
  • 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 copy hello onto it.
  • char *s = "hello"; means give me the address of a printed sign that already says hello.

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.

Take Quiz

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:

  • s is 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:

  • p points 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, not sizeof on 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

  1. The compiler sees the string literal "hello".
  2. That literal represents the character sequence:
'h', 'e', 'l', 'l', 'o', '\0'
  1. For char s[] = "hello";

    • space for 6 characters is created for s
    • those 6 characters are copied into s

    Memory idea:

s: [h][e][l][l][o][\0]
  1. For char *p = "hello";

    • a pointer variable p is created
    • it stores the address of the first character of the string literal

    Memory idea:

p ----> [h][e][l][l][o][\0]
  1. s[1] reads the second element of the array s, which is 'e'.
  2. p[1] reads the second character from the memory p points to, which is also 'e'.
  3. The program prints:
e e

Important detail

Even though both can be used similarly for reading characters, the underlying storage is different:

  • s owns its character data
  • p only 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
Take Quiz

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:

  • %s expects 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

Conceptchar s[] = "hello";char *s = "hello";
What s isArray of charPointer to char
Memory ownershipOwn storage for charactersStores address only
Initial dataCopy of literal placed in arrayPoints to literal
Modifiable charactersYesNo, not safely
Reassign sNoYes
sizeof(s)Size of entire arraySize of pointer
Typical useWritable string bufferReference to read-only text

Related comparison: char * vs const char *

DeclarationMeaningSafe to modify through pointer?
char *s = "hello";Pointer to literal, but type does not express read-only intent clearlyNo
const char *s = "hello";Pointer to read-only textNo

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.

Take Quiz

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.

Take Quiz

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 sizeof results
Take Quiz

Keep learning

Related questions

View more