0

Kiểu bản ghi trong C++

đã đăng vào 31, Tháng 7, 2026, 22:41

KIỂU BẢN GHI TRONG C++

Chào các bạn học sinh và lập trình viên! Khi giải quyết các bài toán quản lý dữ liệu thực tế trên hệ thống Sen Code Online Judge! hoặc trong các đề thi Học sinh giỏi Tin học, chúng ta thường phải xử lý các đối tượng chứa nhiều thông tin phức tạp.

Ví dụ: Để quản lý một học sinh, ta cần lưu trữ: Họ tên (xâu ký tự), Tuổi (số nguyên), Điểm trung bình (số thực). Nếu sử dụng các mảng riêng biệt cho từng thông tin, chương trình sẽ rất khó viết, dễ nhầm lẫn chỉ số và cực kỳ phức tạp khi cần sắp xếp danh sách.

Để giải quyết triệt để vấn đề này, C++ cung cấp Kiểu bản ghi (struct), cho phép chúng ta tự định nghĩa một kiểu dữ liệu mới gom nhóm nhiều thuộc tính khác nhau lại thành một thể thống nhất. Bài viết này sẽ hướng dẫn chi tiết cú pháp và cách áp dụng kiểu dữ liệu bản ghi này.


1. Khái niệm kiểu bản ghi (struct)

Kiểu bản ghi (cấu trúc - struct) là một kiểu dữ liệu do người dùng tự định nghĩa, cho phép kết hợp các biến thuộc các kiểu dữ liệu khác nhau (số nguyên, số thực, ký tự, xâu ký tự, mảng...) lại dưới một tên gọi duy nhất. Mỗi biến thành phần bên trong cấu trúc được gọi là một Trường dữ liệu (Field) hoặc Thành viên (Member).


2. Cú pháp khai báo bản ghi

Cú pháp:
struct Tên_Bản_Ghi {
    kiểu_dữ_liệu_1 tên_trường_1;
    kiểu_dữ_liệu_2 tên_trường_2;
    ...
}; // CHÚ Ý: Bắt buộc phải có dấu chấm phẩy ở cuối
Ví dụ minh họa:

Khai báo một kiểu bản ghi để quản lý thông tin của một Học sinh:

struct HocSinh {
    string maSo;       // Mã số học sinh
    string hoTen;      // Họ và tên
    int tuoi;          // Tuổi
    double diemTB;     // Điểm trung bình học tập
}; // Đã khai báo xong một kiểu dữ liệu mới tên là HocSinh

3. Khai báo biến và truy xuất các trường của bản ghi

a. Khai báo biến kiểu bản ghi:

Sau khi đã định nghĩa cấu trúc, ta sử dụng Tên_Bản_Ghi như một kiểu dữ liệu thông thường (giống như int, double, string...) để khai báo biến:

HocSinh hs1; // Khai báo một học sinh tên hs1
HocSinh hs2 = {"HS01", "Nguyen Van A", 16, 9.25}; // Khai báo và khởi tạo trực tiếp
b. Truy xuất các trường dữ liệu (Đọc / Ghi):

Để truy cập vào các trường thành viên của một biến bản ghi, ta sử dụng Toán tử dấu chấm (.):

tên_biến_bản_ghi.tên_trường
Ví dụ minh họa truy xuất:
#include <bits/stdc++.h>

using namespace std;

// Khai báo struct ở ngoài hàm main để dùng chung toàn chương trình
struct HocSinh {
    string maSo;
    string hoTen;
    int tuoi;
    double diemTB;
};

int main() {
    HocSinh hs;

    // Ghi dữ liệu vào các trường của bản ghi hs
    hs.maSo = "HS102";
    hs.hoTen = "Tran Thi B";
    hs.tuoi = 17;
    hs.diemTB = 9.5;

    // Đọc dữ liệu từ bản ghi hs và in ra màn hình
    cout << "Ma so: " << hs.maSo << "\n";
    cout << "Ho ten: " << hs.hoTen << "\n";
    cout << "Tuoi: " << hs.tuoi << "\n";
    cout << "Diem TB: " << hs.diemTB << "\n";

    return 0;
}

4. Mảng bản ghi và Sắp xếp mảng bản ghi (Rất quan trọng)

Trong các bài toán lập trình thi đấu, ta thường không quản lý một biến đơn lẻ mà quản lý một mảng gồm N bản ghi (ví dụ danh sách N thí sinh, N cuốn sách, N mặt hàng...).

Khai báo mảng bản ghi:
vector<HocSinh> danhSach(N); // Khai báo mảng động vector chứa N học sinh
Sắp xếp mảng bản ghi bằng hàm sort tự định nghĩa:

Hàm sort mặc định của C++ không tự hiểu cách so sánh hai đối tượng phức tạp như HocSinh (nên so sánh theo điểm số hay theo họ tên?).

Do đó, chúng ta phải viết một Hàm so sánh (Comparator) để hướng dẫn sort cách sắp xếp.

Ví dụ minh họa hoàn chỉnh:

Nhập vào danh sách gồm N học sinh. Sắp xếp danh sách học sinh theo thứ tự Điểm trung bình giảm dần. In ra danh sách sau khi sắp xếp.

#include <bits/stdc++.h>

using namespace std;

struct HocSinh {
    string maSo;
    string hoTen;
    double diemTB;
};

// Hàm so sánh tự định nghĩa (Comparator)
// Trả về true nếu học sinh a đứng trước học sinh b
bool soSanhHocSinh(HocSinh a, HocSinh b) {
    return a.diemTB > b.diemTB; // Sắp xếp điểm trung bình giảm dần
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    int N;
    cin >> N;

    vector<HocSinh> ds(N);

    for (int i = 0; i < N; ++i) {
        cin >> ds[i].maSo;
        cin.ignore(); // Xóa khoảng trắng thừa trước khi đọc chuỗi có dấu cách
        getline(cin, ds[i].hoTen);
        cin >> ds[i].diemTB;
    }

    // Gọi hàm sort truyền thêm hàm so sánh tự định nghĩa
    sort(ds.begin(), ds.end(), soSanhHocSinh);

    cout << "--- BANG XEP HANG DIEM SO ---\n";
    for (int i = 0; i < N; ++i) {
        cout << ds[i].maSo << " | " << ds[i].hoTen << " | " << ds[i].diemTB << "\n";
    }

    return 0;
}

5. ĐỀ XUẤT 10 BÀI TẬP THỰC HÀNH THỰC TẾ (KIỂU BẢN GHI)

Hãy thực hiện đọc/ghi dữ liệu bằng tệp thông qua freopen đối với 10 bài tập ứng dụng thực tế đa dạng lĩnh vực sau đây:

Bài 1: Bảng xếp hạng điểm số thí sinh
  • Ngữ cảnh: Hệ thống Sen Code Online Judge! cần thống kê danh sách xếp hạng thí sinh của một kỳ thi. Mỗi thí sinh gồm các thông tin: Mã thí sinh (chuỗi không chứa dấu cách), Họ tên (chuỗi chứa dấu cách), Số bài giải đúng (số nguyên).
  • Yêu cầu: Đọc dữ liệu từ tệp ranking.inp. Hãy sắp xếp danh sách thí sinh giảm dần theo số bài giải đúng. Nếu hai thí sinh có cùng số bài giải đúng bằng nhau, hãy ưu tiên xếp thí sinh có Mã số nhỏ hơn theo thứ tự từ điển đứng trước. Ghi kết quả ra tệp ranking.out.
  • Đầu vào (Input - tệp ranking.inp):
    • Dòng 1 chứa số nguyên dương N (1 <= N <= 104).
    • 3 * N dòng tiếp theo, mỗi nhóm 3 dòng chứa thông tin của một thí sinh: Mã số, Họ tên, Số bài đúng.
  • Đầu ra (Output - tệp ranking.out): Danh sách thí sinh sau khi sắp xếp, mỗi thí sinh gồm Mã số, Họ tên, Số bài đúng cách nhau bởi dấu gạch đứng |.
  • Ví dụ:

    • Input:

      3
      user02
      Tran Van B
      5
      user01
      Nguyen Van A
      5
      user03
      Le Thi C
      3
      
    • Output:

      user01 | Nguyen Van A | 5
      user02 | Tran Van B | 5
      user03 | Le Thi C | 3
      
Bài 2: Doanh thu bán sách nhà sách
  • Ngữ cảnh: Một nhà sách quản lý thông tin các quyển sách bán ra trong tháng. Mỗi quyển sách gồm: Tên sách (chuỗi chứa khoảng trắng), Giá bán lẻ (số nguyên), Số lượng đã bán (số nguyên).
  • Yêu cầu: Đọc dữ liệu từ tệp book.inp. Tính tổng doanh thu của nhà sách trong tháng và tìm quyển sách đem lại doanh thu lớn nhất (Doanh thu = Giá bán * Số lượng bán). Ghi kết quả ra tệp book.out.
  • Đầu vào (Input - tệp book.inp):
    • Dòng 1 chứa số nguyên N (1 <= N <= 1000).
    • N nhóm dòng tiếp theo, mỗi nhóm gồm 3 dòng: Tên sách, Giá bán, Số lượng bán.
  • Đầu ra (Output - tệp book.out):
    • Dòng 1 ghi một số nguyên duy nhất là tổng doanh thu của tất cả các sách.
    • Dòng 2 ghi tên quyển sách đem lại doanh thu cao nhất.
  • Ví dụ:

    • Input:

      2
      Lap Trinh C++ Can Ban
      150000
      10
      Cau Truc Du Lieu Va Giai Thuat
      180000
      20
      
    • Output:

      5100000
      Cau Truc Du Lieu Va Giai Thuat
      
Bài 3: Nhân viên kỳ cựu của công ty
  • Ngữ cảnh: Một công ty cần tìm nhân viên lớn tuổi nhất để tổ chức vinh danh nhân sự kỳ cựu. Thông tin mỗi nhân viên gồm: Mã nhân viên, Họ tên, Năm sinh (số nguyên).
  • Yêu cầu: Đọc thông tin từ tệp employee.inp, tìm nhân viên lớn tuổi nhất (có năm sinh nhỏ nhất) và ghi ra tệp employee.out.
  • Đầu vào (Input - tệp employee.inp):
    • Dòng 1 chứa số nguyên N (1 <= N <= 1000).
    • N nhóm dòng tiếp theo, mỗi nhóm gồm 3 dòng: Mã nhân viên, Họ tên, Năm sinh.
  • Đầu ra (Output - tệp employee.out): Mã nhân viên và Họ tên của nhân viên lớn tuổi nhất cách nhau bởi dấu cách (nếu có nhiều người có cùng năm sinh nhỏ nhất, in ra người đầu tiên xuất hiện trong danh sách).
  • Ví dụ:

    • Input:

      2
      NV01
      Nguyen Van Nam
      1985
      NV02
      Tran Thi Hoa
      1980
      
    • Output: NV02 Tran Thi Hoa

Bài 4: Kiểm tra hàng hết hạn sử dụng
  • Ngữ cảnh: Quản lý kho hàng siêu thị cần lọc ra các mặt hàng đã hết hạn sử dụng tính đến ngày X cho trước để loại bỏ khỏi kệ hàng. Mỗi mặt hàng gồm: Mã hàng, Tên hàng, Ngày hết hạn (lưu dưới dạng số nguyên định dạng YYYYMMDD, ví dụ ngày 15 tháng 8 năm 2026 sẽ lưu là 20260815).
  • Yêu cầu: Đọc danh sách hàng hóa và ngày hiện tại X từ tệp inventory.inp. Lọc ra các sản phẩm đã hết hạn sử dụng (ngày hết hạn < X). Ghi danh sách ra tệp inventory.out.
  • Đầu vào (Input - tệp inventory.inp):
    • Dòng 1 chứa số nguyên N và số nguyên X (1 <= N <= 1000, X là ngày định dạng YYYYMMDD).
    • N nhóm dòng tiếp theo, mỗi nhóm gồm 3 dòng: Mã hàng, Tên hàng, Ngày hết hạn.
  • Đầu ra (Output - tệp inventory.out): Danh sách các mặt hàng hết hạn, mỗi dòng ghi Mã hàng và Tên hàng.
  • Ví dụ:

    • Input:

      2 20260731
      MH01
      Sua tuoi tiet trung
      20260715
      MH02
      Banh bong lan
      20260810
      
    • Output: MH01 Sua tuoi tiet trung

Bài 5: Lọc danh sách bệnh nhân sốt cao
  • Ngữ cảnh: Một bệnh viện cần theo dõi các bệnh nhân có triệu chứng sốt cao. Mỗi bệnh nhân gồm: Mã bệnh nhân, Họ tên, Nhiệt độ cơ thể (số thực). Bệnh nhân bị sốt nếu nhiệt độ từ 37.5 độ C trở lên.
  • Yêu cầu: Đọc dữ liệu bệnh nhân từ tệp patient.inp. Lọc ra các bệnh nhân bị sốt và sắp xếp danh sách theo nhiệt độ giảm dần. Ghi kết quả ra tệp patient.out.
  • Đầu vào (Input - tệp patient.inp):
    • Dòng 1 chứa số nguyên N (1 <= N <= 1000).
    • N nhóm dòng tiếp theo, mỗi nhóm gồm 3 dòng: Mã bệnh nhân, Họ tên, Nhiệt độ.
  • Đầu ra (Output - tệp patient.out): Danh sách bệnh nhân sốt, mỗi dòng gồm Mã bệnh nhân, Họ tên, Nhiệt độ (lấy 1 chữ số thập phân).
  • Ví dụ:

    • Input:

      3
      BN01
      Nguyen Van A
      37.0
      BN02
      Tran Thi B
      38.2
      BN03
      Le Van C
      37.8
      
    • Output:

      BN02 | Tran Thi B | 38.2
      BN03 | Le Van C | 37.8
      
Bài 6: Tọa độ chòm sao gần Trái Đất nhất
  • Ngữ cảnh: Trên bản đồ không gian 2D Oxy với Trái Đất nằm ở gốc tọa độ (0, 0), một nhà thiên văn học quan sát N ngôi sao. Mỗi ngôi sao gồm: Tên ngôi sao, hoành độ x (số nguyên), tung độ y (số nguyên).
  • Yêu cầu: Đọc dữ liệu từ tệp star.inp. Tính khoảng cách Euclid từ mỗi ngôi sao tới Trái Đất (0, 0) bằng công thức: khoảngcách = cănbậc_hai(x2 + y2). Hãy tìm và ghi tên ngôi sao nằm gần Trái Đất nhất ra tệp star.out.
  • Đầu vào (Input - tệp star.inp):
    • Dòng 1 chứa số nguyên N (1 <= N <= 1000).
    • N nhóm dòng tiếp theo, mỗi nhóm gồm 2 dòng: Tên ngôi sao, hai số nguyên x và y.
  • Đầu ra (Output - tệp star.out): Tên ngôi sao gần nhất và khoảng cách làm tròn 2 chữ số thập phân.
  • Ví dụ:

    • Input:

      2
      Sao Kim
      3 4
      Sao Hoa
      5 12
      
    • Output: Sao Kim 5.00

Bài 7: Đặt vé máy bay giá rẻ khởi hành sớm
  • Ngữ cảnh: Khách hàng muốn tìm chuyến bay giá rẻ nhất từ Hà Nội đi TP.HCM. Thông tin mỗi chuyến bay gồm: Số hiệu chuyến bay (chuỗi không khoảng trắng), Giờ khởi hành (chuỗi định dạng HH:MM), Giá vé (số nguyên).
  • Yêu cầu: Đọc dữ liệu từ tệp flight.inp. Tìm chuyến bay có giá vé rẻ nhất. Nếu có nhiều chuyến bay có cùng giá vé rẻ nhất bằng nhau, hãy ưu tiên chọn chuyến bay có giờ khởi hành sớm nhất. Ghi kết quả ra tệp flight.out.
  • Đầu vào (Input - tệp flight.inp):
    • Dòng 1 chứa số nguyên N (1 <= N <= 1000).
    • N nhóm dòng tiếp theo, mỗi nhóm gồm 3 dòng: Số hiệu, Giờ bay, Giá vé.
  • Đầu ra (Output - tệp flight.out): Số hiệu và Giờ bay của chuyến bay được chọn.
  • Ví dụ:

    • Input:

      3
      VN102
      08:30
      1200000
      VJ204
      06:15
      1200000
      QH305
      12:00
      1500000
      
    • Output: VJ204 06:15

Bài 8: Bảng vàng trao huy chương Marathon
  • Ngữ cảnh: Kết quả của giải chạy việt dã marathon ghi nhận thời gian về đích của các vận động viên. Thông tin mỗi vận động viên gồm: Mã số (chuỗi), Họ tên, Thời gian chạy (số nguyên biểu thị số giây).
  • Yêu cầu: Đọc dữ liệu từ tệp marathon.inp. Tìm và in ra thông tin 3 vận động viên đạt huy chương Vàng (thời gian chạy nhanh nhất/nhỏ nhất), Bạc (nhanh nhì), Đồng (nhanh ba) ra tệp marathon.out.
  • Đầu vào (Input - tệp marathon.inp):
    • Dòng 1 chứa số nguyên N (3 <= N <= 1000).
    • N nhóm dòng tiếp theo, mỗi nhóm gồm 3 dòng: Mã số, Họ tên, Thời gian chạy (giây).
  • Đầu ra (Output - tệp marathon.out): 3 dòng tương ứng là thông tin của VĐV đạt giải Vàng, Bạc, Đồng theo định dạng: Giải | Mã số | Họ tên | Thời gian.
  • Ví dụ:

    • Input:

      3
      VDV01
      Nguyen Anh
      7200
      VDV02
      Tran Binh
      7100
      VDV03
      Le Cuong
      7500
      
    • Output:

      Gold | VDV02 | Tran Binh | 7100
      Silver | VDV01 | Nguyen Anh | 7200
      Bronze | VDV03 | Le Cuong | 7500
      
Bài 9: Chu vi đa giác lồi
  • Ngữ cảnh: Một đa giác lồi N đỉnh trên mặt phẳng Oxy được mô tả bằng danh sách tọa độ các đỉnh theo đúng chiều kim đồng hồ. Mỗi đỉnh gồm: Tên đỉnh (1 ký tự), Tọa độ x (số nguyên), Tọa độ y (số nguyên).
  • Yêu cầu: Đọc từ tệp polygon.inp, tính chu vi của đa giác bằng cách cộng khoảng cách giữa các đỉnh liên tiếp (đỉnh 0 đến đỉnh 1, đỉnh 1 đến 2, ..., và đỉnh N-1 quay lại đỉnh 0). Ghi kết quả ra tệp polygon.out.
  • Đầu vào (Input - tệp polygon.inp):
    • Dòng 1 chứa số nguyên N (3 <= N <= 100).
    • N dòng tiếp theo, mỗi dòng chứa ký tự Tên đỉnh và hai số nguyên x, y.
  • Đầu ra (Output - tệp polygon.out): Một số thực duy nhất là chu vi đa giác (lấy chính xác 2 chữ số thập phân).
  • Ví dụ:

    • Input:

      4
      A 0 0
      B 0 3
      C 4 3
      D 4 0
      
    • Output: 14.00 (Các khoảng cách là AB = 3, BC = 4, CD = 3, DA = 4 -> Tổng = 14)

Bài 10: Tối ưu hóa giỏ hàng siêu thị
  • Ngữ cảnh: Một khách hàng đi siêu thị mua sắm với ngân sách tối đa là M đồng. Trong giỏ hàng đã chọn N sản phẩm. Mỗi sản phẩm gồm: Tên sản phẩm, Đơn giá (số nguyên), Số lượng muốn mua (số nguyên).
  • Yêu cầu: Đọc thông tin từ tệp shopping.inp. Kiểm tra xem tổng giá trị giỏ hàng có vượt quá ngân sách M hay không.
    • Nếu không vượt quá, ghi ra số tiền còn dư.
    • Nếu vượt quá ngân sách, hãy ghi ra số tiền thiếu, đồng thời chỉ ra tên sản phẩm có đơn giá cao nhất trong giỏ hàng để khách hàng cân nhắc bỏ lại nhằm tiết kiệm chi phí.
  • Đầu vào (Input - tệp shopping.inp):
    • Dòng 1 chứa số nguyên N và ngân sách M (1 <= N <= 100, M <= 109).
    • N nhóm dòng tiếp theo, mỗi nhóm gồm 3 dòng: Tên sản phẩm, Đơn giá, Số lượng.
  • Đầu ra (Output - tệp shopping.out):
    • Nếu đủ tiền, ghi: DU X DONG (với X là số tiền dư).
    • Nếu thiếu tiền, ghi 2 dòng: dòng 1 ghi THIEU Y DONG (với Y là số tiền thiếu), dòng 2 ghi tên sản phẩm có đơn giá đắt nhất.
  • Ví dụ:

    • Input:

      2 200000
      Dau an Neptune 2L
      120000
      2
      Gao Thom ST25
      190000
      1
      
    • Output:

      THIEU 230000 DONG
      Gao Thom ST25
      

Lời kết

Kiểu bản ghi struct là một vũ khí cực kỳ mạnh mẽ giúp bạn cấu trúc hóa dữ liệu một cách khoa học, sạch sẽ và chuyên nghiệp. Hãy rèn luyện kỹ năng tự định nghĩa các hàm so sánh (Comparator) vì đây là kỹ thuật then chốt xuất hiện liên tục trong lập trình thi đấu thực tế.

Hãy lập trình và nộp bài giải của 10 bài tập kiểu bản ghi trên hệ thống Sen Code Online Judge! nhé! Chúc các bạn học tập thật tốt!


Bình luận

Hãy đọc nội quy trước khi bình luận.


Không có bình luận tại thời điểm này.