Con trỏ và bộ nhớ

Mảng nhiều chiều trong C, C++

Trong bài học này, bạn sẽ duyệt qua một ma trận hai chiều và tính tổng các phần tử trên từng hàng.

Giới thiệu

Mảng hai chiều có thể được hình dung như một bảng gồm nhiều hàng và nhiều cột. Trong cả C và C++, mảng hai chiều được lưu liên tiếp trong bộ nhớ theo thứ tự từng hàng (row-major), nghĩa là toàn bộ hàng đầu tiên được lưu trước, sau đó mới đến hàng thứ hai.

Những điểm quan trọng

  • Con trỏ chỉ được trỏ tới đối tượng còn tồn tại, đúng kiểu dữ liệu, hoặc mang giá trị null.
  • Mỗi vùng nhớ được cấp phát chỉ được giải phóng đúng một lần.
  • Thể hiện rõ hàm nào sở hữu vùng nhớ (ownership) và dữ liệu nào chỉ được đọc (const).

Xem cách hoạt động

Trỏ vào một ô để xem hàng, cột và vị trí của nó trong bộ nhớ, nơi các hàng được lưu nối tiếp nhau.

Code ví dụ bằng C và C++

C
Chạy code →
main.c
#include <stdio.h>

int main(void) {
    int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
    for (int row = 0; row < 2; ++row) {
        int total = 0;
        for (int column = 0; column < 3; ++column) total += matrix[row][column];
        printf("row %d=%d\n", row, total);
    }
    return 0;
}
Kết quả khi chạy
row 0=6
row 1=15
C++
Chạy code →
main.cpp
#include <array>
#include <iostream>

int main() {
    std::array<std::array<int, 3>, 2> matrix{{{{1, 2, 3}}, {{4, 5, 6}}}};
    for (std::size_t row = 0; row < matrix.size(); ++row) {
        int total = 0;
        for (int value : matrix[row]) total += value;
        std::cout << "row " << row << '=' << total << '\n';
    }
}
Kết quả khi chạy
row 0=6
row 1=15

So sánh C và C++

Hai vòng lặp lồng nhau duyệt qua ma trận, vòng ngoài đi qua từng hàng, vòng trong cộng các phần tử của hàng đó vào biến total. Trong C, khi truyền mảng hai chiều vào hàm, bạn phải chỉ rõ kích thước của mọi chiều trừ chiều đầu tiên. Trong C++, std::array giữ thông tin kích thước trong chính kiểu dữ liệu và có phương thức at() để kiểm tra chỉ số có hợp lệ hay không.

C

Việc sở hữu và giải phóng vùng nhớ hoàn toàn do lập trình viên tự đảm bảo.

C++

Container và RAII tự động giải phóng tài nguyên khi đối tượng hết thời gian tồn tại.

Bài tập mở rộng

Hãy chạy cả hai phiên bản, sau đó thử thay đổi code để quan sát sự khác biệt về mức độ an toàn mà mỗi ngôn ngữ mang lại.

  • Thử với con trỏ null và trường hợp cấp phát bộ nhớ thất bại.
  • Chạy chương trình với AddressSanitizer và UndefinedBehaviorSanitizer để phát hiện lỗi bộ nhớ.
  • Ghi chú rõ con trỏ nào sở hữu vùng nhớ và con trỏ nào chỉ dùng để đọc.

Luyện tập với thử thách

Áp dụng kiến thức của bài học này để giải một bài toán thực tế. Mỗi thử thách chạy ngay trên trình duyệt, có kiểm thử tức thì và AI nhận xét.

Tất cả thử thách