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

Lesson 25: Pointer Basics

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

πŸ“– What to learn on this page
βœ… Must-know essentials
  • &x gets address, *p dereferences
  • Declare: int *p = &x;
  • NULL is an explicit "points to nothing" marker
⭐ Read if you have time
  • Pointer arithmetic with arrays
  • Uninitialized vs dangling pointer
  • Pass a pointer to modify the caller's variable
πŸ’ͺ Not getting it first time is normal
Pointers are the biggest wall in C. Almost nobody gets them in one pass.
How to retry
  1. Review arrays β€” rebuild the "contiguous memory" intuition
  2. Re-read this page's memory diagram (house-and-address analogy) several times
  3. Start from the concrete swap function use case
  4. Draw the memory and addresses on paper by hand
  5. It's fine not to grasp it today. Move on, come back in a few days.
πŸ’‘ Tip: Most confusion is about & (take address) vs * (dereference). In a declaration, * marks "pointer type"; in an expression, *p means "what p points to." Nail that distinction.

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.
Note: this diagram assumes a typical 32/64-bit environment where int is 4 bytes. Type sizes are implementation-defined; check with sizeof(int) on your system. Real addresses also vary by OS and run, and variables aren't always laid out this neatly.
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"...
πŸ’‘ Try these ideas too
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 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? How is it different from a dangling pointer?

A. Distinguish these three states:
β‘  NULL pointer: a pointer intentionally set to NULL (value 0), meaning "points to nothing." Dereferencing reliably crashes, so it is a safe detectable sentinel.
β‘‘ Uninitialized pointer: only declared, never assigned. Its value is indeterminate (could be anywhere in memory); dereferencing is undefined.
β‘’ Dangling pointer: a pointer that once referred to a valid object, but that object is no longer valid (freed, went out of scope, etc.). The bits may look valid but any access is undefined behavior.
Defensive habits: initialize at declaration (int *p = NULL;), set p = NULL; right after free(p), and never return the address of a local variable.

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.