Pointers are the single most-asked topic in C interviews — and the topic where most candidates lose the job. This page collects 25 real C pointer interview questions with clear answers and tested, compilable code for every one: pointer basics, const placement, pointer arithmetic, arrays vs pointers, function pointers, dynamic memory, and the predict-the-output traps interviewers love.
Jump to a section: Basics · Pointer Arithmetic · Pointers & Arrays · Pointers & Strings · Pointers & Functions · Dynamic Memory · Predict the Output
Pointer Basics
1. What is a pointer in C?
A pointer is a variable that stores the memory address of another variable. &x gives the address of x; *p (dereferencing) accesses the value stored at the address held in p. Reading or writing through the pointer is reading or writing the original variable:
#include <stdio.h>
int main(void)
{
int x = 42;
int *p = &x; /* p holds the address of x */
printf("x = %d\n", x);
printf("&x = %p\n", (void *)&x);
printf("p = %p\n", (void *)p);
printf("*p = %d\n", *p);
*p = 99; /* modify x through the pointer */
printf("x = %d\n", x);
return 0;
}
Output: x becomes 99 without ever being assigned directly — that’s the whole point (and the whole danger) of pointers. Full walkthrough: Pointers in C — Complete Guide.
2. What is a NULL pointer, and how is it different from an uninitialized pointer?
A NULL pointer deliberately points at nothing — it holds a well-defined “no address” value you can safely test with if (p == NULL) or simply if (!p). An uninitialized (wild) pointer holds garbage: whatever bytes happened to be in that memory. Dereferencing either is undefined behavior, but only the NULL pointer can be detected before the crash. That’s why the idiom is:
int *p = NULL; /* always initialize */
/* ... */
if (p != NULL) {
/* safe to use *p */
}
Interview follow-up: if (p) is exactly equivalent to if (p != NULL) — the standard guarantees a null pointer compares unequal to any valid object address.
3. What is a void pointer and when would you use one?
A void * is a generic pointer — it can hold the address of any object type, but it cannot be dereferenced or used in arithmetic until cast to a concrete type (the compiler doesn’t know the size of what it points to). It’s how C does generic programming: malloc() returns void *, and qsort()/memcpy() accept void * so one function works for every type. Details and pitfalls: Void Pointer in C.
4. Explain the difference between const int *p, int *const p, and const int *const p.
Read right to left from the variable name:
| Declaration | Pointer can move? | Value can change through it? |
|---|---|---|
const int *p |
Yes | No — pointer to const int |
int *const p |
No — const pointer | Yes |
const int *const p |
No | No |
#include <stdio.h>
int main(void)
{
int a = 1, b = 2;
const int *p1 = &a; /* pointer to const int */
int *const p2 = &a; /* const pointer to int */
const int *const p3 = &a; /* const pointer to const */
p1 = &b; /* OK: the pointer itself can move */
*p2 = 5; /* OK: the pointed-to value can change */
printf("%d %d %d\n", *p1, *p2, *p3); /* prints: 2 5 5 */
return 0;
}
Uncommenting *p1 = 9; or p2 = &b; is a compile error — and being able to say which one fails is exactly what the interviewer is checking.
5. What is the size of a pointer? Does it depend on the pointed-to type?
No. A pointer stores an address, so its size depends on the platform, not the type: typically 8 bytes on 64-bit systems, 4 bytes on 32-bit. sizeof(char *), sizeof(int *), and sizeof(double *) are all the same on mainstream platforms. The pointed-to type matters for arithmetic (see Q7), not for storage. Try it yourself: sizeof a Pointer in C.
6. What is a dangling pointer?
A pointer whose target has been destroyed: memory that was free()d, a local variable whose function returned, or an array element past a realloc(). The pointer still holds the old address, so it looks valid — but dereferencing it is undefined behavior that may work in testing and corrupt data in production. The two classic sources are use-after-free and returning a pointer to a local variable (Q18). Defense: set pointers to NULL immediately after free().
Pointer Arithmetic
7. What does p + 1 actually do?
It advances the address by sizeof(*p) bytes, not by 1 byte. Pointer arithmetic is scaled by the pointed-to type — that’s why the type matters:
#include <stdio.h>
int main(void)
{
int arr[4] = {10, 20, 30, 40};
int *p = arr;
printf("p -> %d at %p\n", *p, (void *)p);
printf("p+1 -> %d at %p\n", *(p + 1), (void *)(p + 1));
printf("difference in bytes: %d\n",
(int)((char *)(p + 1) - (char *)p)); /* prints 4 */
return 0;
}
p + 1 lands on the next int (4 bytes away); for a double * it would move 8. This is also why arr[i] and *(arr + i) are identical by definition. Deep dive: Pointer Arithmetic in C.
8. What is the difference between *p++ and (*p)++?
Precedence: postfix ++ binds tighter than *, so *p++ means *(p++) — use the value, then move the pointer. (*p)++ increments the value pointed to; the pointer stays put:
#include <stdio.h>
int main(void)
{
int arr[] = {10, 20, 30};
int *p = arr;
int v;
v = *p++; /* v = 10, THEN p moves to arr[1] */
printf("v=%d, *p=%d\n", v, *p); /* v=10, *p=20 */
p = arr;
v = (*p)++; /* v = 10, THEN arr[0] becomes 11 */
printf("v=%d, arr[0]=%d\n", v, arr[0]); /* v=10, arr[0]=11 */
return 0;
}
This single question probably rejects more candidates than any other on this page. More variants: Pointer Increment vs Value Increment and Pointer Dereference Precedence.
9. Can you subtract two pointers? Can you add them?
Subtract — yes, if both point into the same array: the result is the number of elements between them (type ptrdiff_t from <stddef.h>), not bytes. Add two pointers — no, it’s meaningless and won’t compile; you can only add an integer to a pointer.
int arr[10];
int *first = &arr[0];
int *last = &arr[9];
ptrdiff_t n = last - first; /* 9 elements, not 36 bytes */
Subtracting pointers into different arrays is undefined behavior — a favorite follow-up trap.
Pointers and Arrays
10. Is an array name a pointer? What’s the difference between a and &a?
An array name is not a pointer — it’s an array that converts to a pointer to its first element in most expressions. a and &a print the same address but have different types, which shows up in arithmetic:
#include <stdio.h>
int main(void)
{
int a[5] = {0};
printf("a = %p\n", (void *)a); /* int* */
printf("&a = %p\n", (void *)&a); /* int(*)[5] */
printf("a+1 = %p\n", (void *)(a + 1)); /* +4 bytes */
printf("&a+1 = %p\n", (void *)(&a + 1)); /* +20 bytes */
printf("sizeof a = %lu\n", (unsigned long)sizeof a); /* 20 */
return 0;
}
a + 1 steps one element; &a + 1 steps one whole array. And sizeof a is 20 (the full array), proving a isn’t just a pointer. Full explanation: Array Name as Pointer in C.
11. Why does sizeof(arr) give a different answer inside a function?
Because arrays decay to pointers when passed to functions. The parameter int arr[] is silently rewritten by the compiler to int *arr — the function receives an address, not a copy of the array:
#include <stdio.h>
void show(int *arr)
{
/* arr is a pointer here — the array "decayed" */
printf("inside function: %lu\n", (unsigned long)sizeof(arr));
}
int main(void)
{
int a[10];
printf("in main: %lu\n", (unsigned long)sizeof(a)); /* 40 */
show(a); /* 8 on 64-bit systems */
return 0;
}
Consequence: a function can never discover an array’s length from the pointer alone — you must pass the count as a separate parameter. (This is also why sizeof(arr)/sizeof(arr[0]) only works in the scope where the array was declared.)
12. What is the difference between int *p[3] and int (*p)[3]?
int *p[3] is an array of 3 pointers to int ([] binds tighter than *). int (*p)[3] is a pointer to an array of 3 ints — the parentheses force the * to apply first. The second form is what you get from &a in Q10, and it’s how you pass 2-D arrays: a function taking int m[][3] really takes int (*m)[3].
Pointers and Strings
13. What is the difference between char *s = "hello" and char s[] = "hello"?
char *s = "hello" points at a string literal — read-only memory. char s[] = "hello" declares an array and copies the literal into it — writable, and sizeof s is 6 (five letters + '\0'), not the size of a pointer. Full comparison: char Array vs String Literal and char Pointer in C.
14. Why does modifying a string literal crash?
char *s = "hello";
s[0] = 'H'; /* undefined behavior — typically segfault */
String literals live in a read-only section of the executable (.rodata); the OS maps those pages write-protected, so the write triggers a segmentation fault. The fix is either char s[] = "hello" (your own writable copy) or declaring the pointer const char *s so the compiler rejects the write at build time — which is why modern code should always use const char * for literals.
Pointers and Functions
15. Why doesn’t swap(int a, int b) actually swap anything?
C passes arguments by value — the function gets copies, swaps the copies, and the copies die at return. To modify the caller’s variables you pass their addresses:
#include <stdio.h>
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main(void)
{
int x = 3, y = 7;
swap(&x, &y);
printf("x=%d y=%d\n", x, y); /* x=7 y=3 */
return 0;
}
Background: Pass by Value in C and Swap Using Pointers.
16. What is a function pointer and where is it actually used?
A pointer that stores the address of a function instead of data, letting you choose behavior at runtime. The canonical example every C programmer has already used is qsort()‘s comparator:
#include <stdio.h>
#include <stdlib.h>
int compare_ints(const void *a, const void *b)
{
int x = *(const int *)a;
int y = *(const int *)b;
return (x > y) - (x < y); /* overflow-safe, unlike x - y */
}
int main(void)
{
int arr[] = {42, 7, 19, 3, 88};
int i;
qsort(arr, 5, sizeof(int), compare_ints);
for (i = 0; i < 5; i++)
printf("%d ", arr[i]); /* 3 7 19 42 88 */
printf("\n");
return 0;
}
Other real uses: callback registration, interrupt/signal handlers, and dispatch tables that replace long switch statements. Note the comparator returns (x > y) - (x < y) rather than x - y, which overflows for large values — mentioning that wins bonus points. Deep dive: Function Pointers in C — Callbacks, Dispatch Tables, and qsort.
17. When do you need a pointer to a pointer (int **)?
Whenever a function must modify the caller’s pointer — same pass-by-value rule as Q15, one level up. The classic case is a function that allocates memory for the caller:
#include <stdio.h>
#include <stdlib.h>
/* Without the double pointer, the caller's pointer never changes */
int create_array(int **out, int n)
{
*out = (int *)malloc(n * sizeof(int));
return *out != NULL;
}
int main(void)
{
int *data = NULL;
if (!create_array(&data, 5)) {
fprintf(stderr, "allocation failed\n");
return 1;
}
data[0] = 123;
printf("data[0] = %d\n", data[0]);
free(data);
return 0;
}
If create_array took a plain int *, the malloc result would be assigned to a local copy and data in main would still be NULL. Other uses: linked-list head modification and argv (an array of char *).
18. What is wrong with returning a pointer to a local variable?
The local lives on the stack, and its stack frame is reclaimed the moment the function returns — the returned pointer dangles (Q6). It often appears to work because the bytes haven’t been overwritten yet, then breaks as soon as any other function runs. Fixes: return by value, have the caller pass a buffer, use static storage (with its thread-safety trade-off), or malloc (with the caller owning the free). Worked example: Returning a Pointer to a Local Variable.
19. How do you read a complex declaration like int *(*fp)(int *)?
Start at the identifier and spiral outward, honoring parentheses: fp → (*fp) is a pointer → (*fp)(int *) to a function taking int * → returning int *. So: “fp is a pointer to a function that takes an int pointer and returns an int pointer.” The same rule decodes Q12’s int (*p)[3]. In real code, typedef makes these readable: typedef int *(*handler)(int *);.
Pointers and Dynamic Memory
20. What is the difference between malloc(), calloc(), and realloc()?
| Function | Signature | Initializes? | Use case |
|---|---|---|---|
malloc |
malloc(n) — n bytes |
No — contents are garbage | General allocation |
calloc |
calloc(count, size) |
Yes — all bytes zero | Arrays that must start zeroed; also checks count×size for overflow |
realloc |
realloc(p, n) |
Preserves old contents | Growing/shrinking an existing block |
Two traps interviewers probe: realloc may move the block (old pointers into it dangle), and the safe idiom is tmp = realloc(p, n); if (tmp) p = tmp; — assigning straight to p leaks the original block if realloc fails and returns NULL.
21. What happens if you call free() twice on the same pointer?
Undefined behavior — a double free. In practice it corrupts the allocator’s bookkeeping and is a well-known security vulnerability class (attackers can turn it into arbitrary writes). Freeing a pointer that never came from malloc is equally undefined. The cheap defense: free(p); p = NULL; — because free(NULL) is guaranteed safe and does nothing. Demonstration: Freeing an Invalid Pointer.
22. How do you find memory leaks and pointer bugs in C?
Name real tools, not just “be careful”: Valgrind (valgrind --leak-check=full ./program) reports leaked blocks with the allocation stack trace, plus use-after-free and invalid reads; AddressSanitizer (gcc -fsanitize=address) catches the same classes at ~2× slowdown; and GDB for inspecting a pointer’s value at the moment of the crash. Tutorials: Find Memory Leaks with Valgrind and How to Use GDB.
Predict the Output
23. Pointer offset into an array
#include <stdio.h>
int main(void)
{
int arr[] = {1, 2, 3, 4, 5};
int *p = arr + 2;
printf("%d %d %d\n", *p, *(p - 1), p[1]);
return 0;
}
Output: 3 2 4. p points at arr[2], so *p is 3, *(p - 1) is arr[1] = 2, and p[1] is *(p + 1) = arr[3] = 4. Indexing works on any pointer, not just array names.
24. Walking a string with a char pointer
#include <stdio.h>
int main(void)
{
char *s = "interview";
printf("%c %c %s\n", *s, *(s + 5), s + 5);
return 0;
}
Output: i v view. *s is 'i'; *(s + 5) is the character 'v' (index 5 of i-n-t-e-r-v); and s + 5 passed to %s prints from that position to the terminator: "view". Passing a mid-string pointer to %s is legal and common.
25. The *p++ finale
#include <stdio.h>
int main(void)
{
int a[] = {5, 10, 15};
int *p = a;
printf("%d ", *p++);
printf("%d ", *p);
printf("%d\n", (*p)++);
printf("%d %d\n", a[1], *p);
return 0;
}
Output:
5 10 10 11 11
Trace: *p++ prints 5 and moves p to a[1]; *p prints 10; (*p)++ prints 10 then bumps a[1] to 11; the last line shows both a[1] and *p are now 11 (same object). If you got all three of these right, you’re ready for the pointer round.
Practice More
Reading answers is one thing — recalling them under pressure is another. Drill these until they’re automatic:
- 50 C Aptitude Questions with Answers — predict-the-output practice across all of C
- Pointers in C — Complete Guide with Examples — the full tutorial behind these questions
- Pointer Dereference, Copy a Value Through a Pointer — quick single-concept drills
Practice on the go: our free C Programming Quiz app for Android has 150+ questions across 9 categories — including a dedicated pointers section with explanations for every answer.
As an Amazon Associate we earn from qualifying purchases.
Recommended Book
Chapter 5 of The C Programming Language by Kernighan & Ritchie is still the best 30 pages ever written on pointers — and we’ve solved all 20 of its exercises. Also on Amazon.com.