C Language track

Lesson 09 · Level 3: Advanced

Pointers

From pointer basics to function pointers

Level 3: Advanced5 min readFree lesson

It is the most powerful tool in the C language, and at the same time, it is the cause of the most bugs. Pointers deal not with “values” but with “where the value is (address)”.

Learning Objectives

  • Understand the declaration of pointer variables and the & and * operators.
  • Modify the caller's variables by passing a pointer to the function.
  • We know that array names actually act like pointers.
  • Learn pointer arithmetic and NULL check patterns.

Why do we need pointers? — "Room number of mailbox"

Just as you can send mail without having to go to the house in person, you just need to know “room number”. A pointer is a means of indirect access to a value in memory.

The world of variables The world of pointers
Deals with the value itself Deals with the address where the value is located
“One apple” “Second compartment of the refrigerator”

This indirect approach makes it possible to:

  1. Function modifies the caller’s variable (imitating call by reference)
  2. Pass only the address without copying large data (performance)
  3. Use dynamic memory (runtime size determination, next section)
  4. Linked data structure (linked list, tree)

Key concepts

1) Address and backreference

int x = 42;
int *p = &x;        //p is the address of x
*p = 100;           //Assign 100 to where p points → x = 100

Memory picture:

x (value 42) p (pointer, holds the address of x)
   ┌────────┐                     ┌────────┐
   │   42   │ ◄────────────────── │  &x    │
   └────────┘                     └────────┘
Address: 0x100 Address: 0x200

*p (dereference) = "value pointed to by p" = x.

2) The size of the pointer is independent of the data type.

sizeof(int *)    == 8   // 64-bit
sizeof(char *)   == 8
sizeof(double *) == 8

A pointer only contains address, so it has the same size no matter what data type it points to. However, the data type determines how many bytes *p will read from memory and how to interpret them.

3) Arrays and pointers

int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;          //arr is the address of the first element
p[2];        // 30
*(p + 2);    //30 (equivalent)

Array names are converted to a pointer to the first element within the expression. However, sizeof(arr) is the entire size of the array, and sizeof(p) is the pointer size.

4) NULL pointer

int *p = NULL;
if (p != NULL) *p = 0;     //Always check

Dereferencing an invalid pointer will almost certainly cause a segmentation fault. Use NULL to specify "there is no place to point yet".

See as example

Example 1 — ex01_basic.c: Address and dereference

int x = 42;
int *p = &x;
*p = 100;

Execution result (address varies depending on execution)

Value of x: 42
Address of x: 0x7fffda2960a4
Value of p: 0x7fffda2960a4 (= address of x)
*p: 42 (value pointed to by p)
*p = 100 then x = 100

Key: The address where the value of p and &x are the same. *p = 100 actually replaces x.

Example 2 — ex02_swap.c: Exchange two values with a function

void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }
swap(&x, &y);

Execution result

Before exchange: x=1, y=2
After exchange: x=2, y=1

Bottom line: swap doesn't work if you're passing by value. You must pass the address so that the function can directly modify the caller's variables.

Example 3 — ex03_array_ptr.c: Arrays are in fact pointers

int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;

Execution result

arr[0]=10, *(p+0)=10, p[0]=10
arr[1]=20, *(p+1)=20, p[1]=20
arr[2]=30, *(p+2)=30, p[2]=30
arr[3]=40, *(p+3)=40, p[3]=40
arr[4]=50, *(p+4)=50, p[4]=50
sizeof(arr) = 20
sizeof(p)   = 8

Bottom line: arr[i], *(p+i), p[i] are all the same. But sizeof is different.

Example 4 — ex04_ptr_arith.c: Pointer arithmetic

int arr[] = {100, 200, 300, 400};
int *p = arr;
p++;          //Increase by sizeof(int)
p += 2;

Execution result (address example)

p -> 100 (address 0x7ffee6ef7c40)
p+1 -> 200 (address 0x7ffee6ef7c44)
p+3 -> 400 (address 0x7ffee6ef7c4c)
last - first = 3

Key point: Pointer arithmetic moves in data type size units (4 bytes for int). The result of last - first is difference in number of elements.

A different perspective — variables vs pointers

Using variables only: Using pointers:
   ┌──────┐                              ┌──────┐
│ x=10 │ Function -- (Copy value 10) ─► │ x=10 │ Function -- (Pass address 0x100) ─►
└──────┘ Only copy within the function └──────┘ The function is
Can be replaced directly modify the original

A pointer gives a function the "right to borrow and use the original variable".

Common Mistakes

  1. NULL dereference: int *p = NULL; *p = 0; → segfault.
  2. Uninitialized pointer: int *p; *p = 0; → Random memory corruption (very dangerous).
  3. Return address of local variable: When the function ends, the address is a dangling pointer.
    int *bad(void) { int x = 10; return &x; }   /*danger*/
    
  4. sizeof misunderstanding: If you write sizeof for a pointer, it is the size of the pointer itself, not the size of the array it points to.
  5. & operator missing: Same mistake as scanf("%d", n).

Cleanup

  • A pointer is a “variable that holds an address”. Obtain the address with & and read and write the value with *.
  • If a function wants to change a variable in the caller, it must pass the address.
  • Array names act like a pointer to the first element in an expression.
  • Pointer arithmetic is performed in data type size units.
  • NULL checking and initialization are the first line of defense against memory accidents.

Try it yourself

cd src
gcc -std=c11 -Wall -o ex01 ex01_basic.c     && ./ex01
gcc -std=c11 -Wall -o ex02 ex02_swap.c      && ./ex02
gcc -std=c11 -Wall -o ex03 ex03_array_ptr.c && ./ex03
gcc -std=c11 -Wall -o ex04 ex04_ptr_arith.c && ./ex04

Application:

  • Try what happens when the swap function of ex02 receives only int a, int b parameters.
  • In ex03, create a function int sum(int *a, int n) that calculates the sum by passing arr to the function instead of p.

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/ex01_basic.cC
#include <stdio.h>

int main(void) {
    int x = 42;
    int *p = &x;

    printf("Value of x: %d\n", x);
    printf("Address of x: %p\n", (void*)&x);
    printf("Value of p: %p (= address of x)\n", (void*)p);
    printf("*p: %d (value pointed to by p)\n", *p);

    *p = 100;
    printf("*p = 100 then x = %d\n", x);
    return 0;
}
src/ex02_swap.cC
#include <stdio.h>

void swap(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}

int main(void) {
    int x = 1, y = 2;
    printf("Before exchange: x=%d, y=%d\n", x, y);
    swap(&x, &y);
    printf("After exchange: x=%d, y=%d\n", x, y);
    return 0;
}
src/ex03_array_ptr.cC
#include <stdio.h>

int main(void) {
    int arr[5] = {10, 20, 30, 40, 50};
    int *p = arr;

    for (int i = 0; i < 5; i++) {
        printf("arr[%d]=%d, *(p+%d)=%d, p[%d]=%d\n",
               i, arr[i], i, *(p + i), i, p[i]);
    }

    printf("sizeof(arr) = %zu\n", sizeof(arr));   // 20
    printf("sizeof(p)   = %zu\n", sizeof(p));     // 8 (64-bit)
    return 0;
}
src/ex04_ptr_arith.cC
#include <stdio.h>

int main(void) {
    int arr[] = {100, 200, 300, 400};
    int *p = arr;

    /*Move by the size of the data type pointed to by p.*/
    printf("p -> %d (address %p)\n", *p, (void*)p);
    p++;
    printf("p+1 -> %d (address %p)\n", *p, (void*)p);
    p += 2;
    printf("p+3 -> %d (address %p)\n", *p, (void*)p);

    /*Difference between two pointers = number of elements*/
    int *first = arr, *last = &arr[3];
    printf("last - first = %ld\n", (long)(last - first));
    return 0;
}

Assignment

Problem 1

A function that takes three integers a, b, and c as arguments and returns sorted in ascending order Write void sort3(int *a, int *b, int *c).

Enter: 30 10 20
Sort by: 10 20 30
  • File name: hw01.c

Problem 2

Function to calculate the sum of an array using a pointer Write int sum(const int *arr, int n).

  • File name: hw02.c

Problem 3

Function to double all elements of an integer array Write void doubleAll(int *arr, int n) and print the result.

  • File name: hw03.c
Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

hw01.cC
#include <stdio.h>

static void swap(int *x, int *y) { int t = *x; *x = *y; *y = t; }

void sort3(int *a, int *b, int *c) {
    if (*a > *b) swap(a, b);
    if (*b > *c) swap(b, c);
    if (*a > *b) swap(a, b);
}

int main(void) {
    int a, b, c;
    printf("Enter three integers:");
    scanf("%d %d %d", &a, &b, &c);

    sort3(&a, &b, &c);
    printf("Sort: %d %d %d\n", a, b, c);
    return 0;
}
hw02.cC
#include <stdio.h>

int sum(const int *arr, int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += arr[i];
    return s;
}

int main(void) {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int n = (int)(sizeof(arr) / sizeof(arr[0]));
    printf("Total: %d\n", sum(arr, n));
    return 0;
}
hw03.cC
#include <stdio.h>

void doubleAll(int *arr, int n) {
    for (int i = 0; i < n; i++) arr[i] *= 2;
}

int main(void) {
    int arr[] = {1, 2, 3, 4, 5};
    int n = (int)(sizeof(arr) / sizeof(arr[0]));

    doubleAll(arr, n);
    for (int i = 0; i < n; i++) printf("%d ", arr[i]);
    putchar('\n');
    return 0;
}

Learn this lesson with a mentor

Work through the C Language track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now