Char Pointer in C – Reading a String with *(s + 2)

A char pointer into a string works exactly like an int pointer into an array — except each step is one byte, one character. This question from our C Programming Quiz App asks what *(s + 2) reads out of "abc". Getting it right means keeping two things straight: where a string pointer starts, and that offsets count from zero.

The Quiz Question

char *s = "abc";
printf("%c", *(s + 2));

What is printed by this code?

  1. a
  2. b
  3. c
  4. abc

The Correct Answer: c

The string literal "abc" is stored in memory as four bytes: 'a', 'b', 'c', '\0'. The pointer s holds the address of the first byte. s + 2 advances two characters, landing on 'c', and the dereference reads it. Verified with gcc 13.3 and Apple clang 21, both with -Wall -Wextra clean:

$ gcc -Wall -Wextra str.c && ./a.out
c
  s ──→ [ 'a' ][ 'b' ][ 'c' ][ '\0' ]
         s+0    s+1    s+2    s+3
                        ↑ *(s + 2) reads here

Why Each Wrong Answer Is Wrong

Why not “a”?

'a' is *s — offset zero, no arithmetic. We ran printf("%c %c", *s, *(s + 1)) alongside the quiz line and got a b, confirming the layout above. Choosing “a” means reading the pointer’s starting position and ignoring the + 2.

Why not “b”?

This is the classic off-by-one: counting “first, second” instead of “offset 0, offset 1”. 'b' sits at *(s + 1). Offset 2 is the third character.

Why not “abc”?

Printing the whole string requires the %s conversion with the pointer itself: printf("%s", s). Our code uses %c with a single dereferenced character. Mixing these up the other way — passing *s to %s — is a crash waiting to happen, and both compilers warn about it under -Wall.

char *s vs char s[] — One Difference That Bites

Reading through s is fine, but writing is where char *s = "abc" differs from char s[] = "abc":

char *s = "abc";    /* s points at a string literal: read-only    */
s[0] = 'x';         /* undefined behavior — typically a segfault  */

char t[] = "abc";   /* t is YOUR array, initialized by copy       */
t[0] = 'x';         /* fine: t is now "xbc"                       */

String literals live in read-only storage on modern platforms; the pointer form merely borrows them. The array form copies the characters into writable memory you own. Our quiz code only reads, so both forms would print c — but the distinction matters the moment you modify.

Because *(s + 2) is identical to s[2] (see why pointer arithmetic scales), everything you know about array indexing carries over — this is exactly how functions like strlen walk to the terminating '\0'.

Frequently Asked Questions

Is *(s + 2) the same as s[2]?

Yes — the standard defines s[2] as *(s + 2). For a char pointer each step is exactly one byte, so s + 2 is the third character of the string.

What is the difference between char *s = “abc” and char s[] = “abc”?

The pointer form points at a read-only string literal; writing through it is undefined behavior. The array form copies the literal into a writable array you own. Reading works identically in both.

Why does %c need the dereference but %s doesn’t?

%c expects a character value, so you pass *(s + 2). %s expects a pointer to a NUL-terminated string and walks it itself, so you pass s. Passing the wrong one is a format mismatch both gcc and clang flag with -Wformat.

Related Reading

Recommended Books

This question is #17 in the C Programming Quiz App — 155 questions with explanations covering operators, pointers, memory, 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>