A negative array index looks illegal — surely p[-1] must be undefined behavior? Not necessarily. This question from our C Programming Quiz App catches people who memorized “negative index = bad” instead of the actual rule: what matters is where the resulting address lands, not the sign of the offset.
The Quiz Question
int arr[] = {10, 20, 30};
int *p = arr + 2;
printf("%d", p[-1]);
What is printed by this code?
- 10
- 20
- 30
- Undefined behavior
The Correct Answer: 20
p = arr + 2 parks the pointer on the last element, arr[2]. Since p[-1] is defined as *(p + (-1)), it steps one element back — to arr[1], which is 20. The address stays comfortably inside the array, so the access is fully defined. Verified on gcc 13.3 and Apple clang 21, warning-free under -Wall -Wextra:
$ gcc -Wall -Wextra negidx.c && ./a.out 20
arr: [ 10 ][ 20 ][ 30 ]
p-2 p-1 p ← p points here (arr + 2)
p[-1] = *(p - 1) = arr[1] = 20
Why Each Wrong Answer Is Wrong
Why not 10?
10 is at p[-2] (also perfectly legal from here — it lands on arr[0]). Choosing 10 usually means thinking “negative index wraps to the start”. There’s no wrapping; it’s plain arithmetic backwards from wherever the pointer currently stands.
Why not 30?
30 is *p — the element under the pointer with no offset. This answer ignores the -1 entirely.
Why not “Undefined behavior”?
This is the popular wrong answer, and it’s almost right thinking applied to the wrong case. The rule (C11 §6.5.6): pointer arithmetic is defined as long as the result stays within the array, or one past its end. p - 1 from arr + 2 lands on arr[1] — inside. But move the starting point and the same syntax genuinely is UB:
int *q = arr; /* q at arr[0] */
printf("%d", q[-1]); /* one BEFORE the array: UB */
We ran this on both compilers, and the results are a perfect illustration of undefined behavior’s randomness: clang printed 0, gcc printed 1785567984 — whatever garbage sat below the array on the stack. AddressSanitizer catches it on both:
$ gcc -fsanitize=address negbad.c && ./a.out ==356933==ERROR: AddressSanitizer: stack-buffer-underflow READ of size 4 at 0x720a3dd0001c thread T0
Same expression, different starting pointer — one is well-defined, the other is a stack-buffer-underflow. The sign of the index was never the issue.
Where Negative Indices Are Actually Used
Real code meets p[-1] whenever a pointer walks forward through a buffer and needs to peek at what it just passed: parsers checking the previous character, sliding-window algorithms, or comparing each element to its predecessor (p[0] < p[-1]) inside a sort. As long as the pointer has advanced at least that far into the array, the backward reference is defined — the same rule that makes forward pointer arithmetic safe in-bounds and undefined outside.
Frequently Asked Questions
Is a negative array index always undefined behavior in C?
No. p[-1] is defined whenever the resulting address is still inside the array — for example when p points at element 2, p[-1] is element 1. It’s undefined only when the result falls outside the array, such as arr[-1] from the base.
What does p[-1] actually compute?
By definition p[-1] is *(p + (-1)): step back one element (one sizeof unit, not one byte) from where p points, then read.
How do I catch out-of-bounds negative indexing?
Compile with -fsanitize=address and run: AddressSanitizer reports a stack-buffer-underflow with the exact line. Compilers alone typically can’t warn, because whether p[-1] is in bounds depends on runtime pointer values.
Related Reading
- Pointers in C – Complete Guide
- Use-After-Free in C – Another Memory Bug ASan Catches
- C Program to Insert an Element into an Array
- C Aptitude Questions and Answers
Recommended Books
- The C Programming Language – Kernighan & Ritchie (India) | Amazon.com
- C Programming: A Modern Approach – K.N. King (India) | Amazon.com
This question is #42 in the C Programming Quiz App — 155 questions with explanations covering operators, pointers, memory, and more.
Download on Google Play →