Character Pointers and Strings in C: Memory Layout, Worked Outputs and Exam Traps

Build a memory-first model for character pointers and C strings. Trace GATE to GITE and CAT to CBT, then check literals, bounds, terminators and capacity.

KnowledgeGate Team

Exam prep & CS education

Updated 2 Sep 20266 min read

A one-line C output question can turn on four different ideas: array storage, string-literal storage, the null terminator and operator precedence. Draw the memory cells before tracing the code. In the first trace, a pointer crosses "GATE", records length 4 and changes the array to "GITE". In the second, precedence makes "CAT" become "CBT".

Character pointers and C strings begin with memory cells

char *p may point to one character, a string's first or interior character, or no valid object. The type does not guarantee a valid string.

For char word[] = "GATE";, the array contains five elements:

Offset

word+0

word+1

word+2

word+3

word+4

Stored value

'G'

'A'

'T'

'E'

'\0'

sizeof word is 5; its visible length is 4. Usually word converts to &word[0]. Thus char *p = word, *p = 'G', p[2] = 'T' and *(p+2) = 'T' agree. It does not convert under sizeof or unary &.

The GATE CS Exam Preparation route places this string-focused skill alongside the broader C and data-structures sequence.

Character array versus pointer to a string literal

Declaration

What it means

char a[] = "GATE";

A writable five-element array. a[1] = 'I'; produces "GITE".

const char *q = "GATE";

Points to a literal. q[1] is 'A', but modifying the literal is undefined behaviour.

An array is not assignable, so a = a + 1; is invalid. After char *p = a; p = p + 1;, p-a is 1, *p is 'A', and a still begins at a[0]. sizeof a is 5; sizeof p is a pointer size, not string length.

Read const declarations outwards. const char *movingReader = a may move but cannot change characters. char * const fixedWriter = a cannot move but permits *fixedWriter = 'M'. const char * const fixedReader = "GATE" allows neither.

Character-pointer traversal from GATE to GITE

Trace this C17 program core:

char word[] = "GATE";
char *p = word;
int length = 0;

while (*p != '\0') {
    ++length;
    ++p;
}

p = word + 1;
*p = 'I';
printf("%s %d %td\n", word, length, p - word);

Pointer offset

*p before test

Length before body

Body action

Next offset

0

'G'

0

Increment length and pointer to 1

1

1

'A'

1

Increment length and pointer to 2

2

2

'T'

2

Increment length and pointer to 3

3

3

'E'

3

Increment length and pointer to 4

4

4

'\0'

4

Condition false, stop at offset 4

4

p = word + 1 moves from the terminator to index 1. *p = 'I' makes the cells 'G','I','T','E','\0'. Thus word = "GITE", length = 4, p-word = 1, and the output is:

GITE 4 1

The loop visits four characters and performs four increments. For length n, it takes O(n) time and O(1) extra space; the mutation is O(1).

Five-cell memory trace of the array GATE becoming GITE, showing length 4 and the output GITE 4 1.

strlen, sizeof, pointer subtraction and parameters

For word = "GITE" and p = word+1, strlen(word) = 4, sizeof word = 5, strlen(p) = 3 for "ITE", and p-word = 1. sizeof p is merely a pointer size.

Parameter char s[] becomes char *s, so sizeof s in void inspect(char s[]) measures a pointer. Use void inspect(char *s, size_t n) and call inspect(word, sizeof word). Here n = 5, including \0.

size_t count(const char *s) {
    const char *start = s;
    while (*s != '\0') ++s;
    return (size_t)(s - start);
}

Results are count("GATE") = 4, count(word) = 4 for "GITE", and count(p) = 3. The same distinction scales to array decay and multidimensional row strides in Arrays and Strings in C: Array-to-Pointer Decay, sizeof Traps and 2D Address Arithmetic.

Character-pointer precedence: why CAT becomes CBT

char s[] = "CAT";
char *p = s;

printf("%c ", *p++);
printf("%c ", (*p)++);
printf("%c ", *p);
printf("%s\n", s);

Postfix ++ binds more tightly than unary *, so *p++ is *(p++). It prints 'C', then moves p from index 0 to 1. (*p)++ prints old 'A', stores 'B' in s[1], and keeps p at 1. The third call prints 'B'; %s reads 'C','B','T','\0'.

After call

Cumulative output

Pointer index

String

1

C

1

CAT

2

C A

1

CBT

3

C A B

1

CBT

4

C A B CBT

1

CBT

The exact output is C A B CBT.

Four-step pointer trace of the string CAT, showing how *p++ and (*p)++ produce the output C A B CBT.

Character-pointer traps: terminators, literals and capacity

char bad[4] = {'G','A','T','E'}; is not a C string. %s or strlen searches beyond it for \0, causing undefined behaviour. For char good[5] = {'G','A','T','E','\0'};, strlen(good) = 4 and sizeof good = 5.

word+4 points to the terminator and may be dereferenced. word+5 is one-past, usable for comparison or subtraction, not dereferencing. Arithmetic before word or beyond word+5 is invalid.

Reject char *q = "GATE"; q[0] = 'L'; because modifying a literal is undefined. Use char q[] = "GATE"; q[0] = 'L'; to obtain "LATE", or const char *q = "GATE" for read-only access.

char dest[5] = "GATE"; cannot accept 'S': four characters plus \0 fill it, so strcat(dest, "S") overflows. char dest[6] = "GATE"; works because 4 + 1 + 1 = 6; "GATES" has \0 at index 5. Check capacity first.

char *p; *p = 'A'; uses an indeterminate address. char ch = 'Z'; char *p = &ch; *p = 'A'; instead leaves ch = 'A'.

Character-pointer question patterns and rapid checks

Common forms are output traces, sizeof versus strlen, declarators, and defined versus undefined behaviour. Draw cells including \0, mark the offset, apply precedence and one side effect, then check writable bounds.

char *names[3] is an array of three pointers. char (*table)[5] points to five chars. Given char rows[2][5] = {"GATE", "C"}; table = rows;, table[0][2] = 'T', table[1][0] = 'C', and each row holds 5 chars.

Five quick checks:

  1. For char a[] = "DOG", sizeof a = 4 and strlen(a) = 3.

  2. With char *r = a+1, *r = 'O' and r-a = 1.

  3. After word becomes "GITE", *(word+3) = 'E'.

  4. char *names[3] is an array of pointers, not a pointer to an array.

  5. Modifying a string literal is undefined behaviour.

KnowledgeGate has over 40 live practice questions on Character Pointer (String). For the broader address-versus-value model, double pointers and function arguments, use Pointers in C for GATE: Memory Diagrams and Output Traces. Keep this string-focused checklist for terminators, literal storage, strlen, capacity and pointer precedence.

Character pointers and strings in C: the short version

  • Draw \0.

  • Separate array storage from pointer state.

  • Treat string literals as non-writable.

  • Use strlen for visible length and sizeof only for the object visible in the current scope.

  • Remember that *p++ moves the pointer, while (*p)++ changes the character.

  • Stay within the array and its one-past boundary.

  • Prove destination capacity before copying or appending.

The two outputs are GITE 4 1 and C A B CBT. If either is not immediate, redraw the cells and pointer offsets before attempting timed questions.

Use the C Language Course: Concepts, MCQs and Coding when the gap is the language sequence around arrays, pointers and strings. Use GATE Guidance by Sanchit Sir when this topic needs to fit within a complete GATE CS preparation plan. Reproduce both traces on paper, then change one initial character or pointer offset and predict the result before compiling.