πŸ‡―πŸ‡΅ ζ—₯本θͺž | πŸ‡ΊπŸ‡Έ English
Advertisement

Lesson 25: Pointer Basics

Learn C pointers through memory visualization. Addresses and dereferencing explained with diagrams.

What is a pointer?

A variable is a box in memory. Each box has an address. A pointer is a variable that stores an address.

Memory is a "numbered, contiguous warehouse"

A computer's memory (RAM) is like a long row of shelves, where each byte (8 bits) has a sequential number (address). When you declare a variable, the OS allocates a free slot and assigns it an address.
πŸ’Ύ Memory (RAM) β€” each cell is 1 byte
int n = 10; (4 bytes)
int m = 20; (4 bytes)
char c = 'A'; (1 byte)
Unused
Key point: Start address of n = 0x1000 / Start address of m = 0x1004 / Address of c = 0x1008
Since int uses 4 bytes, the next variable is placed 4 addresses later. Memory is allocated contiguously, sized according to the type.
House and address analogy: A variable is a "house", the address is its "street number". A pointer is a "memo" that records that address. Knowing the address lets you read or rewrite what's inside the house.
Get an address with the & operator: Writing &n gives you the address of variable n (e.g., 0x1000). You can even print the address with printf("%p\n", &n);.

Regular variable vs pointer

Regular variable
int n = 10;
Stores the value (10) directly in the box
Pointer variable
int *p = &n;
Stores the address of n in the box
The declaration form is type *name;. The type is the type that the pointer points to. int *p means "pointer p that points to an int variable".

& operator and * operator

Two operators are essential when working with pointers.
& (address-of operator)
&n β†’ address of n
Gets the address of a variable.
Same & used in scanf!
* (dereference operator)
*p β†’ value at the address p points to
Accesses the value the pointer points to.
Can be used to both read and write.
int n = 10;
int *p = &n; // assign address of n to p
printf("%d", *p); // β†’ 10 (value p points to)
*p = 99; // rewrites n!
printf("%d", n); // β†’ 99

Address and value β€” Visualizer

Use the buttons to see the relationship between variable n and pointer p.
Address 0x1000
int n
β€”
β†’
Address 0x2000
int *p
β€”
Press the buttons in order...

Step through memory line by line

See how the pointer moves inside memory, one line at a time. Pointer p will switch between pointing to n and pointing to m.
Program
int n = 10;
int m = 20;
int *p;
p = &n;
*p = 99;
p = &m;
*p = 77;
πŸ’Ύ Memory (RAM)
0x1000
int n β€”
0x1004
int m β€”
0x2000
int *p β€”
Press "Execute next line"...
What to observe:

The truth about & in scanf

Up to now you've been writing scanf("%d", &a); in scanf. That "&" is the operator that passes an address.
Why is & needed?
The scanf function needs to know where to write the value. That's why you pass the address of the variable.
β†’ Internally, scanf uses a pointer to write the input value at that address.
int a;
scanf("%d", &a); // pass the address of a

// inside scanf, roughly...
// void scanf(char *fmt, int *p){ *p = input_value; }
⚠️ Common mistake: If you forget & and write scanf("%d", a);, scanf treats a's (indeterminate) contents as an address, writing to an unexpected location, which can crash the program.

swap function β€” a classic pointer example

A swap function that exchanges two variables is a classic use case for pointers.
Pass-by-value cannot swap!
void swap(int a, int b){ ... } does not change the caller's variables.
(Only local copies inside the function are swapped.)
// receive pointers and swap the values they point to
void swap(int *x, int *y){
  int t = *x;
  *x = *y;
  *y = t;
}

int main(void){
  int a = 5, b = 10;
  swap(&a, &b); // pass addresses
  printf("%d %d", a, b); // β†’ 10 5
}
Key point: passing an address to a function is pass-by-reference. Arrays passed to functions are automatically pass-by-reference for the same reason.

Pointer arithmetic (adding/subtracting pointers)

Adding or subtracting an integer to a pointer moves the address by the size of the type. This is the key to understanding the relationship between arrays and pointers.
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;        // points to arr[0]

printf("%d\n", *p);       // 10  (arr[0])
printf("%d\n", *(p + 1)); // 20  (arr[1])
printf("%d\n", *(p + 3)); // 40  (arr[3])

p += 2;               // p now points to arr[2]
printf("%d\n", *p);       // 30
p + 1 means "the next element", not "the next byte".
With an int pointer (4 bytes), p + 1 advances the address by 4 bytes.
10
p+0
20
p+1
30
p+2
40
p+3
50
p+4

Array and pointer equivalence

In C, the following are all equivalent.
Array notationPointer notationMeaning
arr[0]*arrFirst element
arr[i]*(arr + i)i-th element
&arr[i]arr + iAddress of i-th element

Pointer subtraction

int arr[5] = {10, 20, 30, 40, 50};
int *p1 = &arr[1];
int *p2 = &arr[4];
printf("%ld\n", p2 - p1);  // 3 (difference in number of elements)
Subtracting two pointers returns "the number of elements between them", not the number of bytes.

The const qualifier β€” Mark values as "unchangeable"

const tells the compiler and other developers that a variable won't be modified. It prevents bugs and makes code intent clear.

Basic: const variables

const int MAX = 100;
printf("%d\n", MAX);  // OK: read freely

MAX = 200;             // ❌ Compile error: cannot modify const
Use cases: Values that never change during execution β€” like pi, array sizes, configuration constants. The advantage over #define is type checking.

Pointers and const β€” 3 patterns

Combining pointers with const is a notoriously confusing topic. The meaning depends on which side of the asterisk the const appears.
DeclarationWhat can changeMeaning
const int *p
or int const *p
βœ… p can change
❌ *p cannot change
Pointer to a constant value
int * const p ❌ p cannot change
βœ… *p can change
Constant pointer (can't point elsewhere)
const int * const p ❌ p cannot change
❌ *p cannot change
Fully locked pointer

Concrete examples

int a = 10, b = 20;

// β‘  const int *p : pointed-to value is read-only
const int *p1 = &a;
p1 = &b;        // βœ… OK: pointer itself can change
*p1 = 30;       // ❌ Error: pointed-to value is immutable

// β‘‘ int * const p : pointer itself is read-only
int * const p2 = &a;
*p2 = 30;       // βœ… OK: can modify 'a' through p2
p2 = &b;        // ❌ Error: p2 can't be redirected

// β‘’ const int * const p : everything locked
const int * const p3 = &a;
*p3 = 30;       // ❌ Error
p3 = &b;        // ❌ Error
Reading trick: Read right-to-left.
・const int *p = "p is a pointer to a const int"
・int * const p = "p is a const pointer to int"

const in function parameters

When passing arrays or strings to functions that won't modify them, use const to make that intent explicit.
// Just reads the string β†’ mark as const
int my_strlen(const char *s) {
    int n = 0;
    while (*s) { s++; n++; }
    return n;
}

// Sums array elements, doesn't modify them β†’ const
int sum(const int arr[], int n) {
    int total = 0;
    for (int i = 0; i < n; i++) total += arr[i];
    return total;
}
Best practice: Always mark parameters as const when the function doesn't modify them.
・Signals intent: "this function won't touch your data"
・Accidental modifications are caught at compile time
・Standard library functions like strlen, strcmp follow this convention

String literals and const

char *s1 = "Hello";        // ⚠️ Discouraged (some compilers warn)
s1[0] = 'J';                // ❌ Undefined behavior!

const char *s2 = "Hello";  // βœ… Correct way
// s2[0] = 'J';  ← caught at compile time

char s3[] = "Hello";         // βœ… Array: modification OK
s3[0] = 'J';                // β†’ "Jello"
Important: String literals (strings written directly in source like "Hello") live in read-only memory. Always receive them via const char *.
Summary: const acts as a "bug-prevention barrier". Your code still works without it, but using it catches mistakes at compile time and makes your intent crystal clear.

Try it yourself β€” pointers

A program that rewrites a value through a pointer. Go ahead and run it.
pointer.c
Output
Press "Run"...
Confirm that *p = 99; rewrites the value of n. That is what it means to "indirectly change a value via a pointer".
Advertisement

Related lessons

Functions
Lesson 24: Arrays as function arguments
How to pass an array to a function in C, and its connection to pointers.
Advanced
Lesson 28: Dynamic memory (malloc/free)
Dynamic allocation in C. malloc, free, memory leak causes and fixes.
Advanced
Lesson 26: Structures (struct)
Defining a struct in C, accessing members, and combining with arrays.
← Previous lesson
Lesson 24: Arrays as function arguments
Next lesson β†’
Lesson 26: Structures (struct)

Frequently Asked Questions (FAQ)

Q. Why use pointers? Aren't regular variables enough?

A. Pointers are needed to: 1. Modify a variable's value from a function (pass-by-reference), 2. Dynamically allocate memory, 3. Build complex data structures (lists, trees, etc.), 4. Work with strings, and more. Many features are impossible without pointers.

Q. I keep confusing * and &. Which is which?

A. & (address-of) takes the address indicating "where the variable lives". * (dereference) goes to see "what's stored at this address". In a pointer declaration `int *p;`, the * means "p is a pointer to int".

Q. What is a NULL pointer?

A. A NULL pointer represents "a pointer that points to nothing", with value 0. An uninitialized pointer is called a dangling pointer and is dangerous. For safer code, get in the habit of always initializing pointers at declaration, e.g., `int *p = NULL;`.

Q. What does "a pointer to a pointer" mean?

A. A pointer is just a variable too, so the pointer variable itself lives in memory. That means you can make "a pointer to a pointer": `int **pp = &p;`. Multi-level pointers are tricky, but you're just applying the same principle repeatedly. In practice they're rarely needed.

Review Quiz

Check your understanding of this lesson!

Q1. What is stored in p of int *p;?

An integer value
The address of an int variable
A string

int *p is a pointer to int, which stores the memory address of an int variable.

Q2. With int x=10; int *p=&x;, what is the value of *p?

The address of x
10
The address of pointer p

*p dereferences the pointer. Since p points to x, *p = 10.

Q3. What happens when you dereference (*p) a NULL pointer?

Returns 0
Runtime error (segmentation fault)
Compile error

Dereferencing a NULL pointer causes a runtime error such as a segmentation fault. Always NULL-check before use.

Share this article
Share on X (Twitter) Share on Facebook Share on LinkedIn Share on Reddit

Recommended books to deepen this lesson

Use this site to understand the mechanics, then practice with a book for the best results

πŸ“˜
The C Programming Language (2nd ed.)
Brian W. Kernighan, Dennis M. Ritchie
Known as K&R. The original C reference. A great step up once you've finished the basics.
View on Amazon
πŸ“—
C Programming: A Modern Approach
K. N. King
Rich explanations and thorough exercises. Widely adopted as a university textbook.
View on Amazon
πŸ“™
Understanding and Using C Pointers
Richard Reese
Focused specifically on pointers. Clears up a lot of confusion around memory.
View on Amazon

* These are affiliate links. Purchases help support the site.