Advanced Pointers

Pointers are like address slips — a pointer to a pointer is an address slip written on another address slip. It sounds convoluted, but in real life, "the locker number where you store a package" works exactly this way, layer upon layer.

Pointer to Pointer

A pointer variable is itself a variable — it lives in memory and has its own address. When you use a pointer to point to another pointer, you get a "pointer to pointer."

C
int x = 42;
int *p = &x;
int **pp = &p;

Through **pp you can indirectly modify the value of x:

C
**pp = 100;
printf("%d\n", x);
TEXT
100

The most common use of a double pointer is "modifying the pointer itself inside a function," such as having a function allocate memory for a pointer:

C
void alloc_buf(char **ptr, int size) {
    *ptr = (char *)malloc(size);
}

int main(void) {
    char *buf = NULL;
    alloc_buf(&buf, 128);
    if (buf) {
        strcpy(buf, "hello");
        printf("%s\n", buf);
        free(buf);
    }
    return 0;
}
TEXT
hello
💡 Tip: If you only pass a single pointer char *ptr, the function modifies a copy — the original pointer outside won't change. To modify the pointer itself, you must pass its address.

Pointer Array vs. Array Pointer

These two concepts are easily confused. The key is the precedence of * and [].

Pointer Array

int *arr[4] — binds with [] first, so it's an array whose elements are int *.

C
int a = 10, b = 20, c = 30;
int *arr[3] = {&a, &b, &c};
for (int i = 0; i < 3; i++) {
    printf("%d ", *arr[i]);
}
TEXT
10 20 30

A typical application: an array of strings.

C
const char *names[3] = {"Alice", "Bob", "Carol"};
for (int i = 0; i < 3; i++) {
    printf("%s\n", names[i]);
}
TEXT
Alice
Bob
Carol

Array Pointer

int (*p)[4] — binds with * first, so it's a pointer pointing to an array of 4 ints. Commonly used for passing 2D arrays to functions.

C
int matrix[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};
int (*p)[4] = matrix;
printf("%d\n", p[1][2]);
TEXT
7

Example

C
#include <stdio.h>

void print_matrix(int (*m)[4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%3d", m[i][j]);
        }
        printf("\n");
    }
}

int main(void) {
    int matrix[2][4] = {
        {10, 20, 30, 40},
        {50, 60, 70, 80}
    };
    print_matrix(matrix, 2);

    const char *fruits[3] = {"apple", "banana", "cherry"};
    for (int i = 0; i < 3; i++) {
        printf("%s ", fruits[i]);
    }
    printf("\n");
    return 0;
}
▶ Try it Yourself
TEXT
 10 20 30 40
 50 60 70 80
apple banana cherry
⚠️ Note: Mnemonic: int *a[4] is a pointer array (an array of pointers), int (*a)[4] is an array pointer (a pointer to an array).

Function Pointer and Callback

A function name is the entry address of that function. A function pointer can store this address, enabling "deferred calls" or "callbacks."

Function Pointer Declaration

C
int (*pf)(int, int);

This declares pf as a pointer to a function that takes two ints and returns an int.

C
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }

int main(void) {
    int (*pf)(int, int) = add;
    printf("%d\n", pf(3, 5));
    pf = sub;
    printf("%d\n", pf(10, 4));
    return 0;
}
TEXT
8
6

Callback Mechanism

Pass a function pointer as an argument to another function, so the latter can "call it back" at the right time.

C
void process(int *arr, int len, int (*transform)(int)) {
    for (int i = 0; i < len; i++) {
        arr[i] = transform(arr[i]);
    }
}

int double_it(int n) { return n * 2; }
int negate(int n) { return -n; }

int main(void) {
    int data[4] = {1, 2, 3, 4};
    process(data, 4, double_it);
    for (int i = 0; i < 4; i++) printf("%d ", data[i]);

    printf("\n");
    process(data, 4, negate);
    for (int i = 0; i < 4; i++) printf("%d ", data[i]);
    return 0;
}
TEXT
2 4 6 8
-2 -4 -6 -8

Example

C
#include <stdio.h>
#include <stdlib.h>

int cmp_asc(const void *a, const void *b) {
    return *(int *)a - *(int *)b;
}

int cmp_desc(const void *a, const void *b) {
    return *(int *)b - *(int *)a;
}

void sort_and_print(int *arr, int n, int (*cmp)(const void *, const void *)) {
    qsort(arr, n, sizeof(int), cmp);
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main(void) {
    int nums[5] = {42, 7, 19, 3, 55};
    sort_and_print(nums, 5, cmp_asc);

    int nums2[5] = {42, 7, 19, 3, 55};
    sort_and_print(nums2, 5, cmp_desc);
    return 0;
}
▶ Try it Yourself
TEXT
3 7 19 42 55
55 42 19 7 3
🔥 Common Mistake: The standard library's qsort is a classic application of function pointer callbacks — you only need to provide the comparison rule, and the sorting algorithm takes care of the rest.

Three Combinations of const and Pointers

const combined with pointers has three positions, each with different semantics.

Pointer to const

C
const int *p;
int const *p;

You cannot modify the pointed-to value through p, but p itself can point elsewhere.

C
int a = 10, b = 20;
const int *p = &a;
printf("%d\n", *p);
p = &b;
printf("%d\n", *p);
TEXT
10
20

const Pointer

C
int * const p = &a;

p cannot change what it points to, but you can modify the value through p.

C
int a = 10;
int * const p = &a;
*p = 99;
printf("%d\n", a);
TEXT
99

const Pointer to const

C
const int * const p = &a;

Neither the pointer's target nor the value it points to can be changed — the strictest read-only.

💡 Tip: Quick trick: look at whether const is to the left or right of *. On the left, it modifies the data; on the right, it modifies the pointer itself.

void Pointer

void * is a "generic pointer" that can point to any type, but you must cast it before use.

C
int a = 42;
double b = 3.14;
void *p;

p = &a;
printf("%d\n", *(int *)p);

p = &b;
printf("%.2f\n", *(double *)p);
TEXT
42
3.14

Typical uses of void *:

⚠️ Note: You cannot dereference a void * — the compiler doesn't know the size of the data it points to. You must first cast it to a concrete pointer type.

❓ FAQ

Q How do you quickly distinguish between a pointer array and an array pointer?
A Look at what the variable name binds to first — if it binds with [] first, it's an array (pointer array); if it binds with * first, it's a pointer (array pointer). Use parentheses to change precedence.
Q What's the difference between a function pointer and a function name?
A A function name is the function's address — it's a constant. A function pointer is a variable that can store different function addresses, enabling runtime switching of the call target.
Q Are const int *p and int const *p the same?
A Yes, completely identical. Both mean a pointer to const int — you cannot modify the data through p, but p itself can point elsewhere.
Q Can you perform pointer arithmetic on a void pointer?
A No. The compiler doesn't know the size of the data a void pointer references, so the stride of p+1 is undefined. You must first cast it to a concrete type before doing arithmetic.

📖 Summary

📝 Exercises

  1. Write a function void swap_ptr(int a, int b) that swaps what two pointers point to. Verify in main that each pointer points to the other's original value after the swap
  2. Declare a function pointer array int (*ops[4])(int,int) containing add, subtract, multiply, and divide functions. Call the corresponding operation based on a user-input index
  3. Write a generic print function void print_any(void *data, char type) where type is 'i' for int, 'd' for double, and 's' for string
100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏