Chapter 20Advanced

Pointers in C

The most powerful feature in C! Pointers store memory addresses. Learn what they are, why they matter, and how to use them safely.

25 min readUpdated 2024-12-16
pointersmemory address&*dereferenceNULLpointer declaration

What You Will Learn

  • Understand what pointers are (address holders)
  • Use & to get address, * to get value
  • Declare and initialize pointers
  • Understand NULL pointers and why they matter

01What is a Pointer?

A pointer is a variable that stores the memory address of another variable. Instead of holding data directly, it "points to" where the data is stored in memory.

Variable vs Pointer

Regular Variable

int x42

Stores the actual value

Pointer Variable

int *ptr0x1000
42

Stores the address of another variable

Why Use Pointers?

  • Efficiency: Pass large data without copying
  • Dynamic Memory: Allocate memory at runtime
  • Data Structures: Build linked lists, trees, graphs
  • Function Parameters: Modify variables from functions

🏠 Real-World Analogy: House Addresses

Think of memory like a street with houses:

Regular Variable = House Contents

Like the furniture inside a house. You store actual things (data) in it.

Pointer = House Address

Like a piece of paper with an address written on it. It doesn't contain furniture, but tells you where to find it!

Just like you can give someone your address (pointer) instead of moving your entire house (copying data), pointers let you share location instead of duplicating data!

02Understanding Memory Addresses

Every variable in your program is stored at a specific location in memory. This location has a unique address (like a house number on a street).

memory_address.c
C
1#include <stdio.h>
2
3int main() {
4 int age = 25;
5 float price = 99.99;
6 char grade = 'A';
7
8 // Print the VALUE of variables
9 printf("age = %d\n", age);
10 printf("price = %.2f\n", price);
11 printf("grade = %c\n", grade);
12
13 // Print the ADDRESS of variables using & operator
14 printf("\nAddress of age: %p\n", (void*)&age);
15 printf("Address of price: %p\n", (void*)&price);
16 printf("Address of grade: %p\n", (void*)&grade);
17
18 return 0;
19}

Sample Output:

age = 25
price = 99.99
grade = A

Address of age: 0x7ffd5e8e3a4c
Address of price: 0x7ffd5e8e3a48
Address of grade: 0x7ffd5e8e3a47

The & Operator (Address-of)

&variable returns the memory address of a variable. The address is shown in hexadecimal format (0x...).

03Declaring and Initializing Pointers

pointer_declare.c
C
1#include <stdio.h>
2
3int main() {
4 // Step 1: Create a regular variable
5 int num = 42;
6
7 // Step 2: Declare a pointer (use * in declaration)
8 int *ptr; // ptr can store address of an int
9
10 // Step 3: Assign address to pointer (use & to get address)
11 ptr = &num; // ptr now "points to" num
12
13 // Combined declaration and initialization
14 int value = 100;
15 int *p = &value; // Declare and initialize in one line
16
17 // Print to verify
18 printf("num = %d\n", num); // Value: 42
19 printf("&num = %p\n", (void*)&num); // Address of num
20 printf("ptr = %p\n", (void*)ptr); // Same address (ptr stores it)
21 printf("*ptr = %d\n", *ptr); // Value at that address: 42
22
23 return 0;
24}

🔍 How This Program Works

1

int num = 42 — Creates a variable storing 42 at some memory address (e.g., 0x1000).

2

int *ptr — Declares a pointer variable. The * means "pointer to int". Currently contains garbage.

3

ptr = &num — The & gets num's address (0x1000). Now ptr stores that address.

4

printf("%p", ptr) — Prints what ptr contains: the address 0x1000 (same as &num).

5

*ptr — The * dereferences: "go to address in ptr and get the value there" → returns 42.

Pointer Syntax Summary

SyntaxMeaningExample
int *ptr;Declare pointer to intCreates pointer variable
&numGet address of numReturns 0x7ffd...
ptr = &num;Store address in ptrptr points to num
*ptrGet value at address (dereference)Returns 42

04Dereferencing Pointers (*)

Dereferencing means accessing the value stored at the address the pointer holds. Use the * operator.

dereferencing.c
C
1#include <stdio.h>
2
3int main() {
4 int x = 10;
5 int *ptr = &x; // ptr points to x
6
7 // Reading via pointer
8 printf("Value of x: %d\n", x); // Direct: 10
9 printf("Value via *ptr: %d\n", *ptr); // Via pointer: 10
10
11 // Modifying via pointer
12 *ptr = 50; // Changes the value at address (changes x!)
13
14 printf("\nAfter *ptr = 50:\n");
15 printf("Value of x: %d\n", x); // Now 50!
16 printf("Value via *ptr: %d\n", *ptr); // Also 50
17
18 // Both x and *ptr refer to the same memory location
19 x = 100;
20 printf("\nAfter x = 100:\n");
21 printf("Value via *ptr: %d\n", *ptr); // 100
22
23 return 0;
24}

Output:

Value of x: 10
Value via *ptr: 10

After *ptr = 50:
Value of x: 50
Value via *ptr: 50

After x = 100:
Value via *ptr: 100

🔍 How Dereferencing Works

1

int *ptr = &x — ptr now holds x's memory address. Both x and *ptr refer to the same location.

2

*ptr (reading) — "Go to the address stored in ptr and read what's there" → returns 10.

3

*ptr = 50 (writing) — "Go to the address stored in ptr and write 50 there". This changes x!

4

After *ptr = 50, printing x shows 50 — proof that x and *ptr are the same memory.

5

Changing x = 100 also makes *ptr return 100. They're always in sync!

The * Has Two Meanings!

  • In declaration: int *ptr; — defines a pointer
  • In expression: *ptr — dereferences (gets value)

05Pointer Memory Layout

A pointer is itself a variable that takes up memory. On most modern systems, pointers are 8 bytes (64-bit) or 4 bytes (32-bit).

int x = 42; int *ptr = &x;

Variable x

42

@0x1000

4 bytes

points to

Pointer ptr

0x1000

@0x2000

8 bytes (64-bit)

ptr stores the address 0x1000, which is where x lives in memory

pointer_size.c
C
1#include <stdio.h>
2
3int main() {
4 int x = 42;
5 int *ptr = &x;
6
7 printf("Size of int: %zu bytes\n", sizeof(int)); // 4
8 printf("Size of int*: %zu bytes\n", sizeof(int*)); // 8 (on 64-bit)
9 printf("Size of ptr: %zu bytes\n", sizeof(ptr)); // 8
10
11 // All pointer types are the same size!
12 char *cp;
13 double *dp;
14 printf("\nSize of char*: %zu bytes\n", sizeof(cp)); // 8
15 printf("Size of double*: %zu bytes\n", sizeof(dp)); // 8
16
17 return 0;
18}

06Pointer Arithmetic

You can add or subtract integers from pointers. The pointer moves by the size of the data type it points to.

pointer_arithmetic.c
C
1#include <stdio.h>
2
3int main() {
4 int arr[5] = {10, 20, 30, 40, 50};
5 int *ptr = arr; // Points to first element
6
7 printf("ptr points to: %d (address: %p)\n", *ptr, (void*)ptr);
8
9 // ptr + 1 moves by sizeof(int) = 4 bytes
10 ptr++; // or ptr = ptr + 1
11 printf("After ptr++: %d (address: %p)\n", *ptr, (void*)ptr);
12
13 ptr++;
14 printf("After ptr++: %d (address: %p)\n", *ptr, (void*)ptr);
15
16 // Can also use pointer arithmetic directly
17 printf("\nUsing arr pointer arithmetic:\n");
18 printf("*(arr + 0) = %d\n", *(arr + 0)); // 10
19 printf("*(arr + 1) = %d\n", *(arr + 1)); // 20
20 printf("*(arr + 2) = %d\n", *(arr + 2)); // 30
21
22 // arr[i] is equivalent to *(arr + i)
23 printf("\narr[3] = %d, *(arr + 3) = %d\n", arr[3], *(arr + 3));
24
25 return 0;
26}

Pointer Arithmetic Visualization

10

+0

20

+4

30

+8

40

+12

50

+16

ptr → ptr+1 → ptr+2 → ptr+3 → ptr+4

Each step moves by sizeof(int) = 4 bytes

07Pointers and Arrays

Arrays and pointers are closely related. The array name is actually a pointer to its first element!

pointer_array.c
C
1#include <stdio.h>
2
3int main() {
4 int arr[5] = {10, 20, 30, 40, 50};
5
6 // Array name IS a pointer to first element
7 printf("arr = %p\n", (void*)arr);
8 printf("&arr[0] = %p\n", (void*)&arr[0]); // Same address!
9
10 // These are equivalent ways to access elements:
11 printf("\nAccessing arr[2]:\n");
12 printf("arr[2] = %d\n", arr[2]); // Subscript notation
13 printf("*(arr + 2) = %d\n", *(arr + 2)); // Pointer arithmetic
14
15 // Using a separate pointer
16 int *ptr = arr; // No & needed - arr is already an address
17
18 printf("\nUsing pointer:\n");
19 for (int i = 0; i < 5; i++) {
20 printf("ptr[%d] = %d, *(ptr + %d) = %d\n",
21 i, ptr[i], i, *(ptr + i));
22 }
23
24 return 0;
25}

Key Equivalences

  • arr[i]*(arr + i)
  • &arr[i]arr + i
  • arr&arr[0]

08NULL Pointer

A NULL pointer is a pointer that doesn't point to any valid memory location. It's used to indicate "no value" or "not pointing to anything".

null_pointer.c
C
1#include <stdio.h>
2#include <stdlib.h> // For NULL
3
4int main() {
5 int *ptr = NULL; // Initialize to NULL
6
7 // Always check before dereferencing!
8 if (ptr == NULL) {
9 printf("ptr is NULL - cannot dereference!\n");
10 } else {
11 printf("Value: %d\n", *ptr);
12 }
13
14 // Now point to something valid
15 int x = 42;
16 ptr = &x;
17
18 if (ptr != NULL) {
19 printf("ptr is valid, value: %d\n", *ptr);
20 }
21
22 // Common pattern: set to NULL after freeing
23 // free(ptr); // If dynamically allocated
24 ptr = NULL; // Prevent dangling pointer
25
26 return 0;
27}

⚠️ Never Dereference NULL!

Dereferencing a NULL pointer causes a segmentation fault (crash). Always check if a pointer is NULL before using *ptr.

09Common Pointer Mistakes

❌ Uninitialized Pointer (Wild Pointer)

main.c
C
int *ptr; // Points to random memory!
*ptr = 10; // CRASH - undefined behavior
// Always initialize:
int *ptr = NULL; // Safe

❌ Dangling Pointer

main.c
C
int *ptr;
{
int x = 10;
ptr = &x;
} // x is destroyed here!
printf("%d", *ptr); // UNDEFINED - x no longer exists

❌ Mismatched Pointer Types

main.c
C
float f = 3.14;
int *ptr = &f; // WARNING - type mismatch!
// Correct:
float *ptr = &f; // Types must match

❌ Forgetting & in scanf

main.c
C
int x;
scanf("%d", x); // WRONG! Missing &
scanf("%d", &x); // Correct - pass address

10Summary

What You Learned:

  • Pointer: Variable that stores memory address
  • & operator: Gets address of a variable
  • * operator: Dereferences (gets value at address)
  • Pointer size: 8 bytes on 64-bit, 4 bytes on 32-bit
  • Pointer arithmetic: ptr+1 moves by sizeof(type)
  • NULL: Special value meaning "points to nothing"

11Next Steps

Continue learning about other derived data types: