25 C Pointer Interview Questions and Answers (with Code)

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:

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.

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>