Pointer Basics
I. In-Class Exercises
Programming Exercises
II. Knowledge Summary
✨ Core Concepts of Pointers
A pointer is a special variable that can only store memory addresses, denoted by the * symbol. Pointers are a very important concept in C++ that allow us to directly manipulate memory for more flexible data processing.
Here is an example to understand the core principle of pointers:
int a = 42;
int *p = &a;1Variable Memory Address Stored Value
2-------- -------------- ------------
3a 0x1000 42
4p 0x2000 0x1000 ──────┐
5 │
6 ┌──────────────────────────────┘
7 │ p stores the address of a
8 ▼
9 0x1000 → 42 (*p can access the value of a)Pointer
pis itself a variable with its own address (0x2000), but its content is not ordinary data — it is the address of another variablea(0x1000). By dereferencing with*p, we can follow this address to find the value 42 ofa.
✨ Pointer Definition
When defining a pointer, you need to specify the data type that the pointer points to and the pointer name.
Common pointer definitions:
int *ptr_i; // pointer can only point to memory storing int data
double *ptr_d; // pointer can only point to memory storing double data
char *ptr_c; // pointer can only point to memory storing char data
bool *ptr_b; // pointer can only point to memory storing bool data✨ Pointer Assignment
There are three common ways to assign a pointer:
Assign to a null pointer, indicating the pointer does not point to any valid address:
int *ptr = nullptr;Assign to the memory address of an existing variable using the address-of operator &:
int number = 5;
int *ptr = &number;Assign to a dynamically allocated memory address using the new keyword:
int *ptr = new int;✨ Using Pointers
Using the Address Stored in a Pointer
To use the address stored in a pointer, simply output the pointer directly.
int number = 5;
int *ptr = &number;
cout << ptr << endl; // outputs the address of the variable numberUsing the Data at the Address Stored in a Pointer
To access the data stored at the address held by a pointer, use pointer dereferencing — add a * before the pointer name.
int number = 5;
int *ptr = &number;
cout << *ptr << endl; // outputs the data stored in number, which is 5Using the Pointer Variable's Own Address
To get the address of the pointer variable itself, use the address-of operator by adding & before the pointer name.
int number = 5;
int *ptr = &number;
cout << &ptr << endl; // outputs the address of the ptr variable✨ Passing by Pointer
Passing by pointer means passing the address of the actual argument to the function parameter. Through pointer operations, the actual argument's value can be modified.
Advantages:
- Achieves an effect similar to pass by reference by passing pointers
- Suitable for passing variable-length arrays or dynamically allocated memory
Disadvantages:
- Requires handling pointer validity and memory management issues, which can easily lead to memory leaks or illegal access
- The syntax is relatively complex and error-prone
1void foo(int *x) {
2 *x = 20;
3}
4
5int main() {
6 int a = 10;
7 foo(&a);
8 // the value of a has been modified to 20
9 return 0;
10}✨ Dynamic Memory
Dynamic memory refers to memory dynamically allocated at runtime, created using new and released using delete.
int *ptr = new int(3);
*ptr = 5;
delete ptr;
ptr = nullptr;Important notes for dynamic memory usage:
- After use, the memory must always be released using
delete - After releasing the dynamic memory pointed to by a pointer, the pointer must be set to
nullptr; otherwise it becomes a dangling pointer / wild pointer
Advantages of using dynamic memory:
- Allows flexible allocation of memory
- Saves memory space
- Can be used to implement and handle complex, large data structures
- Increases program scalability
- Can support multithreaded programming
✨ Execution Examples for Pointers
The following memory diagrams illustrate how pointers work.
Example 1: Pointer Pointing to an Existing Variable
int number = 42;
int *ptr = &number;Variable Memory Address (illustration) Stored Value
-------- ---------------------------- ------------
number 0x1000 42
ptr 0x2000 0x1000 (stores the address of number)1Operations:
2cout << number; -> outputs 42 (directly access the variable's value)
3cout << &number; -> outputs 0x1000 (get the variable's address)
4cout << ptr; -> outputs 0x1000 (the address stored in the pointer)
5cout << *ptr; -> outputs 42 (dereference: access the value at the address the pointer points to)
6cout << &ptr; -> outputs 0x2000 (the pointer variable's own address)Example 2: Modifying a Variable's Value Through a Pointer
int a = 10;
int *p = &a;
*p = 20; // modify the value of a through the pointer
cout << a; // outputs 20, the value of a has been modifiedBefore execution: After executing *p = 20:
a [0x1000]: 10 a [0x1000]: 20
p [0x2000]: 0x1000 p [0x2000]: 0x1000 (unchanged)The meaning of
*p = 20is: find the address thatppoints to (0x1000), and modify the value at that address to 20. Sinceais at that address, the value ofaalso changes.
Example 3: Execution Process of Passing by Pointer
1void swap(int *x, int *y) {
2 int temp = *x;
3 *x = *y;
4 *y = temp;
5}
6
7int main() {
8 int a = 3, b = 7;
9 swap(&a, &b);
10 // now a=7, b=3
11}1Before call: a=3 (address 0x1000), b=7 (address 0x1004)
2Call swap(&a, &b): x=0x1000, y=0x1004
3
4Execute temp = *x; -> temp = 3
5Execute *x = *y; -> value at address 0x1000 changed to 7, i.e., a=7
6Execute *y = temp; -> value at address 0x1004 changed to 3, i.e., b=3
7
8After return: a=7, b=3 -> swap successful!Example 4: Lifecycle of Dynamic Memory
int *ptr = new int(100); // allocate memory on the heap, storing value 100
cout << *ptr; // outputs 100
*ptr = 200; // modify to 200
delete ptr; // release memory
ptr = nullptr; // avoid dangling pointerAfter new int(100): ptr -> [heap memory: 100]
After *ptr = 200: ptr -> [heap memory: 200]
After delete ptr: ptr -> [released memory] <- dangerous! ptr is now a dangling pointer
After ptr = nullptr: ptr -> [null] <- safe✨ Problem-Solving Steps for Pointers
General approach for solving problems using pointers:
- Clarify the purpose: Determine whether pointers are needed — typically used in these situations:
- Modifying an external variable's value inside a function (passing by pointer)
- Dynamically allocating memory (determining array size at runtime)
- Directly operating on array elements through addresses
- Define and initialize the pointer: After defining a pointer, always initialize it — either point it to a valid address or set it to
nullptr - Use the pointer: Dereference with
*to access or modify data; use the pointer name directly to get the address - Release memory: If
newwas used, alwaysdeleteafter use and set the pointer tonullptr
✨ Common Mistakes with Pointers
- Using an uninitialized pointer: Using a pointer directly after defining it without assigning a value (e.g.,
int *p; *p = 5;) — the pointer points to a random address, causing the program to crash - Confusing the two meanings of
*: In a definition,int *pmeans "this is a pointer"; in usage,*pmeans "dereference, access the value the pointer points to". The two meanings are different - Forgetting the address-of operator
&: Forgetting to add&when assigning a variable to a pointer, e.g.,int *p = number;should beint *p = &number; - Not setting to null after delete: Forgetting to set the pointer to
nullptrafter releasing dynamic memory — using the pointer again afterward causes undefined behavior (dangling/wild pointer) - Mismatching new and delete: A single variable allocated with
newshould be released withdelete; an array allocated withnew[]should be released withdelete[]. They must not be mixed