Skip to content

Bài 14: Con trỏ cơ bản (Basic Pointers)

📖 Giới thiệu

Con trỏ (Pointer) là một trong những khái niệm quan trọng và mạnh mẽ nhất của C++. Con trỏ là một biến đặc biệt lưu trữ địa chỉ bộ nhớ của một biến khác, thay vì lưu trữ giá trị trực tiếp.

Con trỏ cho phép chúng ta:

  • Quản lý bộ nhớ động: Cấp phát và giải phóng memory khi cần
  • Tăng hiệu suất: Truyền địa chỉ thay vì copy dữ liệu lớn
  • Linh hoạt trong cấu trúc dữ liệu: Linked list, tree, graph
  • Truy cập hardware: Direct memory manipulation
  • Function pointers: Gọi hàm thông qua pointer

Các ứng dụng quan trọng:

  • Dynamic data structures: Linked list, binary tree
  • Memory optimization: Avoid unnecessary copying
  • System programming: Device drivers, embedded systems
  • Algorithms: Efficient sorting and searching
  • Graphics programming: Direct pixel manipulation

🔧 Cú pháp

Khai báo và khởi tạo

cpp
// Khai báo pointer
int* ptr;           // Pointer to int
double* dPtr;       // Pointer to double
char* cPtr;         // Pointer to char

// Khởi tạo pointer
int number = 42;
int* ptr = &number; // ptr lưu địa chỉ của number

// Null pointer
int* nullPtr = nullptr; // C++11 (tốt hơn NULL)
int* oldNullPtr = NULL; // C-style (tránh sử dụng)

// Pointer to pointer
int** ptrToPtr = &ptr;

Toán tử cơ bản

cpp
int value = 100;
int* ptr = &value;

// & (Address-of operator): Lấy địa chỉ
cout << "Dia chi cua value: " << &value << endl;

// * (Dereference operator): Lấy giá trị tại địa chỉ
cout << "Gia tri qua pointer: " << *ptr << endl;

// Thay đổi giá trị qua pointer
*ptr = 200; // value bây giờ là 200

// Kiểm tra pointer
if (ptr != nullptr) {
    cout << "Pointer hop le: " << *ptr << endl;
}

Pointer Arithmetic

cpp
int arr[] = {10, 20, 30, 40, 50};
int* ptr = arr; // Trỏ đến phần tử đầu tiên

// Di chuyển pointer
ptr++;          // Trỏ đến arr[1]
ptr += 2;       // Trỏ đến arr[3]
ptr--;          // Trỏ đến arr[2]

// Truy cập phần tử
cout << *ptr << endl;        // arr[2] = 30
cout << *(ptr + 1) << endl;  // arr[3] = 40
cout << ptr[1] << endl;      // Tương đương *(ptr + 1)

// Khoảng cách giữa pointers
int* start = &arr[0];
int* end = &arr[4];
cout << "Khoang cach: " << (end - start) << endl; // 4

🔬 Phân tích & Giải thích chi tiết

Memory Layout

cpp
#include <iostream>
using namespace std;

void explainMemoryLayout() {
    int value = 42;
    int* ptr = &value;
    
    cout << "=== MEMORY LAYOUT ===" << endl;
    cout << "value = " << value << endl;
    cout << "Dia chi cua value (&value): " << &value << endl;
    cout << "Gia tri cua ptr: " << ptr << endl;
    cout << "Dia chi cua ptr (&ptr): " << &ptr << endl;
    cout << "Gia tri tai dia chi ptr tro toi (*ptr): " << *ptr << endl;
    
    cout << "\nKich thuoc:" << endl;
    cout << "sizeof(int): " << sizeof(int) << " bytes" << endl;
    cout << "sizeof(int*): " << sizeof(int*) << " bytes" << endl;
    cout << "sizeof(ptr): " << sizeof(ptr) << " bytes" << endl;
}

Pass by Pointer vs Pass by Reference

cpp
// Pass by Value
void passByValue(int x) {
    x = 100; // Không ảnh hưởng đến biến gốc
}

// Pass by Pointer
void passByPointer(int* x) {
    if (x != nullptr) {
        *x = 100; // Thay đổi biến gốc
    }
}

// Pass by Reference  
void passByReference(int& x) {
    x = 100; // Thay đổi biến gốc
}

void demonstratePassingMethods() {
    int value = 42;
    
    cout << "Gia tri ban dau: " << value << endl;
    
    passByValue(value);
    cout << "Sau pass by value: " << value << endl; // 42
    
    passByPointer(&value);
    cout << "Sau pass by pointer: " << value << endl; // 100
    
    value = 42; // Reset
    passByReference(value);
    cout << "Sau pass by reference: " << value << endl; // 100
}

Các lỗi thường gặp

cpp
// ❌ Lỗi 1: Uninitialized pointer
void badExample1() {
    int* ptr; // Pointer chưa khởi tạo
    *ptr = 10; // Nguy hiểm! Undefined behavior
}

// ❌ Lỗi 2: Null pointer dereference
void badExample2() {
    int* ptr = nullptr;
    cout << *ptr << endl; // Crash! Segmentation fault
}

// ❌ Lỗi 3: Dangling pointer
int* badExample3() {
    int local = 42;
    return &local; // Nguy hiểm! local sẽ bị hủy
}

// ✅ Cách đúng
void goodExamples() {
    // Luôn khởi tạo pointer
    int value = 42;
    int* ptr = &value;
    
    // Kiểm tra null trước khi dereference
    if (ptr != nullptr) {
        cout << *ptr << endl;
    }
    
    // Không trả về địa chỉ của local variable
    static int staticVar = 42;
    int* safePtr = &staticVar; // OK
}

💻 Ví dụ minh họa

Ví dụ 1: Hệ thống quản lý danh sách liên kết đơn giản

cpp
#include <iostream>
#include <string>
using namespace std;

struct Student {
    int id;
    string name;
    float grade;
    Student* next; // Pointer đến student tiếp theo
    
    Student(int i, const string& n, float g) 
        : id(i), name(n), grade(g), next(nullptr) {}
};

class StudentList {
private:
    Student* head; // Pointer đến node đầu tiên
    int count;
    
public:
    StudentList() : head(nullptr), count(0) {}
    
    // Thêm student vào đầu danh sách
    void addStudent(int id, const string& name, float grade) {
        Student* newStudent = new Student(id, name, grade);
        newStudent->next = head;
        head = newStudent;
        count++;
        cout << "Da them sinh vien: " << name << endl;
    }
    
    // Tìm student theo ID
    Student* findStudent(int id) {
        Student* current = head;
        while (current != nullptr) {
            if (current->id == id) {
                return current;
            }
            current = current->next;
        }
        return nullptr;
    }
    
    // Xóa student theo ID
    bool removeStudent(int id) {
        if (head == nullptr) return false;
        
        // Nếu phần tử đầu cần xóa
        if (head->id == id) {
            Student* temp = head;
            head = head->next;
            delete temp;
            count--;
            return true;
        }
        
        // Tìm phần tử cần xóa
        Student* current = head;
        while (current->next != nullptr) {
            if (current->next->id == id) {
                Student* temp = current->next;
                current->next = temp->next;
                delete temp;
                count--;
                return true;
            }
            current = current->next;
        }
        
        return false;
    }
    
    // In danh sách
    void printList() {
        if (head == nullptr) {
            cout << "Danh sach trong!" << endl;
            return;
        }
        
        cout << "\n=== DANH SACH SINH VIEN ===" << endl;
        cout << "ID\tTen\t\tDiem" << endl;
        cout << "------------------------" << endl;
        
        Student* current = head;
        while (current != nullptr) {
            cout << current->id << "\t" 
                 << current->name << "\t\t" 
                 << current->grade << endl;
            current = current->next;
        }
        cout << "Tong so: " << count << " sinh vien" << endl;
    }
    
    // Tính điểm trung bình
    float calculateAverage() {
        if (head == nullptr) return 0.0f;
        
        float sum = 0.0f;
        Student* current = head;
        while (current != nullptr) {
            sum += current->grade;
            current = current->next;
        }
        
        return sum / count;
    }
    
    // Tìm sinh viên có điểm cao nhất
    Student* findTopStudent() {
        if (head == nullptr) return nullptr;
        
        Student* topStudent = head;
        Student* current = head->next;
        
        while (current != nullptr) {
            if (current->grade > topStudent->grade) {
                topStudent = current;
            }
            current = current->next;
        }
        
        return topStudent;
    }
    
    // Sắp xếp theo điểm (Bubble sort với pointers)
    void sortByGrade() {
        if (head == nullptr || head->next == nullptr) return;
        
        bool swapped;
        do {
            swapped = false;
            Student* current = head;
            
            while (current->next != nullptr) {
                if (current->grade < current->next->grade) {
                    // Swap data (không swap pointers)
                    swap(current->id, current->next->id);
                    swap(current->name, current->next->name);
                    swap(current->grade, current->next->grade);
                    swapped = true;
                }
                current = current->next;
            }
        } while (swapped);
        
        cout << "Da sap xep danh sach theo diem giam dan!" << endl;
    }
    
    // Destructor để giải phóng memory
    ~StudentList() {
        Student* current = head;
        while (current != nullptr) {
            Student* temp = current;
            current = current->next;
            delete temp;
        }
    }
};

int main() {
    StudentList list;
    int choice;
    
    // Thêm một số dữ liệu mẫu
    list.addStudent(1001, "Nguyen Van A", 8.5f);
    list.addStudent(1002, "Tran Thi B", 9.2f);
    list.addStudent(1003, "Le Van C", 7.8f);
    
    do {
        cout << "\n=== QUAN LY SINH VIEN ===" << endl;
        cout << "1. Them sinh vien" << endl;
        cout << "2. Tim sinh vien" << endl;
        cout << "3. Xoa sinh vien" << endl;
        cout << "4. In danh sach" << endl;
        cout << "5. Tinh diem trung binh" << endl;
        cout << "6. Tim sinh vien gioi nhat" << endl;
        cout << "7. Sap xep theo diem" << endl;
        cout << "0. Thoat" << endl;
        cout << "Lua chon: ";
        cin >> choice;
        
        switch (choice) {
            case 1: {
                int id;
                string name;
                float grade;
                
                cout << "Nhap ID: ";
                cin >> id;
                cout << "Nhap ten: ";
                cin.ignore();
                getline(cin, name);
                cout << "Nhap diem: ";
                cin >> grade;
                
                list.addStudent(id, name, grade);
                break;
            }
            
            case 2: {
                int id;
                cout << "Nhap ID can tim: ";
                cin >> id;
                
                Student* found = list.findStudent(id);
                if (found != nullptr) {
                    cout << "Tim thay: " << found->name 
                         << " (Diem: " << found->grade << ")" << endl;
                } else {
                    cout << "Khong tim thay sinh vien!" << endl;
                }
                break;
            }
            
            case 3: {
                int id;
                cout << "Nhap ID can xoa: ";
                cin >> id;
                
                if (list.removeStudent(id)) {
                    cout << "Da xoa sinh vien!" << endl;
                } else {
                    cout << "Khong tim thay sinh vien de xoa!" << endl;
                }
                break;
            }
            
            case 4:
                list.printList();
                break;
                
            case 5:
                cout << "Diem trung binh lop: " << list.calculateAverage() << endl;
                break;
                
            case 6: {
                Student* top = list.findTopStudent();
                if (top != nullptr) {
                    cout << "Sinh vien gioi nhat: " << top->name 
                         << " (Diem: " << top->grade << ")" << endl;
                } else {
                    cout << "Danh sach trong!" << endl;
                }
                break;
            }
            
            case 7:
                list.sortByGrade();
                break;
                
            case 0:
                cout << "Tam biet!" << endl;
                break;
                
            default:
                cout << "Lua chon khong hop le!" << endl;
        }
        
    } while (choice != 0);
    
    return 0;
}

Ví dụ 2: Xử lý mảng với pointers

cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
using namespace std;

// Hoán đổi hai giá trị qua pointer
void swapValues(int* a, int* b) {
    if (a != nullptr && b != nullptr) {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
}

// Tìm min/max trong mảng
void findMinMax(int* arr, int size, int** min, int** max) {
    if (arr == nullptr || size == 0) {
        *min = *max = nullptr;
        return;
    }
    
    *min = *max = &arr[0];
    
    for (int i = 1; i < size; i++) {
        if (arr[i] < **min) {
            *min = &arr[i];
        }
        if (arr[i] > **max) {
            *max = &arr[i];
        }
    }
}

// Sắp xếp mảng bằng pointer (Selection sort)
void selectionSort(int* arr, int size) {
    for (int i = 0; i < size - 1; i++) {
        int* minPtr = &arr[i];
        
        for (int j = i + 1; j < size; j++) {
            if (arr[j] < *minPtr) {
                minPtr = &arr[j];
            }
        }
        
        if (minPtr != &arr[i]) {
            swapValues(&arr[i], minPtr);
        }
    }
}

// Đảo ngược mảng bằng pointers
void reverseArray(int* arr, int size) {
    int* start = arr;
    int* end = arr + size - 1;
    
    while (start < end) {
        swapValues(start, end);
        start++;
        end--;
    }
}

// Tìm kiếm nhị phân với pointers
int* binarySearch(int* arr, int size, int target) {
    int* left = arr;
    int* right = arr + size - 1;
    
    while (left <= right) {
        int* mid = left + (right - left) / 2;
        
        if (*mid == target) {
            return mid;
        } else if (*mid < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    
    return nullptr;
}

// Tính tổng sử dụng pointer arithmetic
int calculateSum(int* arr, int size) {
    int sum = 0;
    int* end = arr + size;
    
    for (int* ptr = arr; ptr < end; ptr++) {
        sum += *ptr;
    }
    
    return sum;
}

// In mảng với thông tin địa chỉ
void printArrayWithAddresses(int* arr, int size) {
    cout << "\n=== MANG VOI DIA CHI ===" << endl;
    cout << "Index\tValue\tAddress\t\tPointer Offset" << endl;
    cout << "------------------------------------------------" << endl;
    
    for (int i = 0; i < size; i++) {
        cout << i << "\t" << arr[i] << "\t" 
             << (arr + i) << "\t" << i * sizeof(int) << endl;
    }
}

// Tạo mảng ngẫu nhiên
void generateRandomArray(int* arr, int size, int minVal = 1, int maxVal = 100) {
    random_device rd;
    mt19937 gen(rd());
    uniform_int_distribution<> dis(minVal, maxVal);
    
    for (int i = 0; i < size; i++) {
        arr[i] = dis(gen);
    }
}

// Menu functions
void testSwap() {
    int a, b;
    cout << "Nhap hai so: ";
    cin >> a >> b;
    
    cout << "Truoc khi hoan doi: a = " << a << ", b = " << b << endl;
    swapValues(&a, &b);
    cout << "Sau khi hoan doi: a = " << a << ", b = " << b << endl;
}

void testArrayOperations() {
    const int MAX_SIZE = 20;
    int arr[MAX_SIZE];
    int size;
    
    cout << "Nhap kich thuoc mang (max " << MAX_SIZE << "): ";
    cin >> size;
    
    if (size > MAX_SIZE || size <= 0) {
        cout << "Kich thuoc khong hop le!" << endl;
        return;
    }
    
    int choice;
    cout << "1. Nhap thu cong, 2. Tao ngau nhien: ";
    cin >> choice;
    
    if (choice == 1) {
        cout << "Nhap " << size << " phan tu: ";
        for (int i = 0; i < size; i++) {
            cin >> arr[i];
        }
    } else {
        generateRandomArray(arr, size);
        cout << "Da tao mang ngau nhien!" << endl;
    }
    
    cout << "\nMang ban dau: ";
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    // Hiển thị thông tin địa chỉ
    printArrayWithAddresses(arr, size);
    
    // Tìm min/max
    int* minPtr;
    int* maxPtr;
    findMinMax(arr, size, &minPtr, &maxPtr);
    
    cout << "\nGia tri nho nhat: " << *minPtr 
         << " tai dia chi " << minPtr << endl;
    cout << "Gia tri lon nhat: " << *maxPtr 
         << " tai dia chi " << maxPtr << endl;
    
    // Tính tổng
    int sum = calculateSum(arr, size);
    cout << "Tong cac phan tu: " << sum << endl;
    cout << "Trung binh: " << (double)sum / size << endl;
    
    // Sắp xếp
    cout << "\nSap xep mang..." << endl;
    selectionSort(arr, size);
    cout << "Mang sau khi sap xep: ";
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    // Tìm kiếm
    int target;
    cout << "Nhap gia tri can tim: ";
    cin >> target;
    
    int* found = binarySearch(arr, size, target);
    if (found != nullptr) {
        cout << "Tim thay " << target << " tai dia chi " << found 
             << " (chi so " << (found - arr) << ")" << endl;
    } else {
        cout << "Khong tim thay " << target << endl;
    }
    
    // Đảo ngược
    cout << "\nDao nguoc mang..." << endl;
    reverseArray(arr, size);
    cout << "Mang sau khi dao nguoc: ";
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    int choice;
    
    do {
        cout << "\n=== DEMO POINTER OPERATIONS ===" << endl;
        cout << "1. Test hoan doi gia tri" << endl;
        cout << "2. Thao tac voi mang" << endl;
        cout << "0. Thoat" << endl;
        cout << "Lua chon: ";
        cin >> choice;
        
        switch (choice) {
            case 1:
                testSwap();
                break;
            case 2:
                testArrayOperations();
                break;
            case 0:
                cout << "Tam biet!" << endl;
                break;
            default:
                cout << "Lua chon khong hop le!" << endl;
        }
        
    } while (choice != 0);
    
    return 0;
}

🏋️ Thực hành

Bài tập 1: Quản lý mảng động

cpp
#include <iostream>
using namespace std;

class DynamicArray {
private:
    int* data;
    int size;
    int capacity;
    
public:
    DynamicArray(int initialCapacity = 10) {
        capacity = initialCapacity;
        size = 0;
        data = new int[capacity];
    }
    
    ~DynamicArray() {
        delete[] data;
    }
    
    void push_back(int value) {
        if (size >= capacity) {
            resize();
        }
        data[size++] = value;
    }
    
    int& at(int index) {
        if (index < 0 || index >= size) {
            throw out_of_range("Index out of range");
        }
        return data[index];
    }
    
    void print() {
        for (int i = 0; i < size; i++) {
            cout << data[i] << " ";
        }
        cout << endl;
    }
    
private:
    void resize() {
        capacity *= 2;
        int* newData = new int[capacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        delete[] data;
        data = newData;
    }
};

Bài tập 2: Matrix với pointers

cpp
#include <iostream>
using namespace std;

class Matrix {
private:
    int** data;
    int rows, cols;
    
public:
    Matrix(int r, int c) : rows(r), cols(c) {
        data = new int*[rows];
        for (int i = 0; i < rows; i++) {
            data[i] = new int[cols]();
        }
    }
    
    ~Matrix() {
        for (int i = 0; i < rows; i++) {
            delete[] data[i];
        }
        delete[] data;
    }
    
    int& operator()(int i, int j) {
        return data[i][j];
    }
    
    void print() {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                cout << data[i][j] << " ";
            }
            cout << endl;
        }
    }
};

📋 Tóm tắt

Các điểm quan trọng

  1. Pointer basics:

    • Lưu trữ địa chỉ memory
    • & để lấy địa chỉ, * để dereference
    • Luôn kiểm tra null trước khi dereference
  2. Pointer arithmetic:

    • Cộng/trừ với integers
    • So sánh pointers
    • Khoảng cách giữa pointers
  3. Memory safety:

    • Khởi tạo pointers đúng cách
    • Tránh dangling pointers
    • Tránh memory leaks

Best Practices

  • Initialize pointers: Luôn khởi tạo với giá trị hợp lệ hoặc nullptr
  • Check before dereference: if (ptr != nullptr)
  • Avoid pointer arithmetic errors: Kiểm tra bounds
  • Match new/delete: Mỗi new phải có delete tương ứng
  • Use smart pointers: Trong C++ hiện đại

Chuẩn bị cho bài tiếp theo

Bài tiếp theo chúng ta sẽ học về Dynamic Memory Allocation:

  • newdelete operators
  • Memory leaks và cách tránh
  • RAII principle
  • Smart pointers introduction

Pointers là nền tảng quan trọng để hiểu memory management và data structures nâng cao!

Khóa học C++ miễn phí