Bỏ qua điều hướng, tới nội dung chính
Học C
Bài 60.226 phút đọc

Level 2: Quản lý sinh viên

Sau bài này bạn sẽ làm được

  • Cài mảng động tăng gấp đôi, không giới hạn cứng
  • Sắp xếp bằng qsort theo nhiều tiêu chí, tăng và giảm
  • Ghi tệp an toàn bằng ghi tạm rồi đổi tên
  • Chạy sạch dưới công cụ phát hiện lỗi bộ nhớ

Dự án thứ hai là bài kiểm tra toàn diện đầu tiên: quản lý một danh sách sinh viên không giới hạn, tìm, sắp, và lưu ra tệp mà không mất dữ liệu. Nó gom mọi thứ của Phần 3 tới 9 vào một chương trình chạy được.

#Mục tiêu

Cấu trúc dữ liệu
typedef struct {
    char  mssv[16], ho_ten[64], lop[16];
    int   nam_sinh;
    char  gioi_tinh;
    float diem[5], diem_tb;
    char  xep_loai[16];
} SinhVien;
Chức năngKỹ thuậtChương
Danh sách không giới hạnMảng động realloc gấp đôiPhần 6
Thêm, sửa, xoá, xemCRUD với kiểm tra hợp lệPhần 3 tới 5
Tìm theo MSSVTìm kiếm nhị phânPhần 8
Tìm theo tênChuỗi con, không phân biệt hoa thườngPhần 4
Sắp xếp nhiều tiêu chíqsort với nhiều hàm so sánhPhần 8
Lưu và tảiTệp nhị phân và CSVPhần 9

#Mảng động

dsv.c, mảng động tăng gấp đôi
typedef struct { SinhVien *pt; size_t co, suc; } DsSV;

static int ds_them(DsSV *d, SinhVien sv) {
    if (d->co == d->suc) {
        size_t moi = d->suc ? d->suc * 2 : 4;       /* gap doi, bat dau 4 */
        SinhVien *p = realloc(d->pt, moi * sizeof *p);
        if (!p) return -1;                          /* het bo nho, GIU cai cu */
        d->pt = p;
        d->suc = moi;
    }
    d->pt[d->co++] = sv;
    return 0;
}
terminal
./dsv_thu
suc chua sau 4 lan them: 4 (bat dau 4)
theo diem giam: An(9.0) Dung(8.5) Chi(7.5) Binh(6.0)
theo ten a-z:   An Binh Chi Dung

#Sắp xếp nhiều tiêu chí

Nhiều hàm so sánh cho qsort
static int theo_diem_giam(const void *a, const void *b) {
    float x = ((const SinhVien *)a)->diem_tb;
    float y = ((const SinhVien *)b)->diem_tb;
    return (x < y) - (x > y);           /* giam dan, KHONG dung x - y */
}

static int theo_ten(const void *a, const void *b) {
    return strcmp(((const SinhVien *)a)->ho_ten,
                  ((const SinhVien *)b)->ho_ten);
}

/* Dung: */
qsort(d.pt, d.co, sizeof *d.pt, theo_diem_giam);
qsort(d.pt, d.co, sizeof *d.pt, theo_ten);

#Ghi tệp an toàn

  1. Ghi vào tệp TẠM

    Không ghi thẳng vào dulieu.dat. Ghi vào dulieu.dat.tmp trước.
  2. Kiểm tra ghi thành công

    Kiểm tra mọi fwrite trả về đúng số phần tử, và fclose trả 0. Nếu lỗi, xoá tệp tạm và báo.
  3. Đổi tên nguyên tử

    rename("dulieu.dat.tmp", "dulieu.dat"). Trên cùng một hệ tệp, đổi tên là nguyên tử: tệp cũ hoặc tệp mới, không bao giờ nửa vời.

#Yêu cầu

Tự làm thử

  1. Mảng động tăng gấp đôi, không giới hạn cứng.
  2. CRUD đầy đủ, kiểm tra hợp lệ: MSSV không trùng, điểm 0 tới 10, năm sinh hợp lệ.
  3. Tìm theo MSSV bằng tìm kiếm nhị phân, tìm theo tên bằng chuỗi con không phân biệt hoa thường.
  4. Sắp xếp qsort nhiều tiêu chí, cả tăng và giảm.
  5. Lưu và tải tệp nhị phân, cộng xuất và nhập CSV.
  6. Thống kê: theo lớp, theo xếp loại, top N, danh sách cảnh báo.
  7. Chia thành ít nhất năm tệp .c và .h cộng Makefile.
  8. Ghi tệp an toàn bằng ghi tạm rồi đổi tên.
  9. Chạy sạch dưới Valgrind và ASan.

Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.

#Coi là xong khi

Một tệp khổng lồ
// main.c 2000 dong: struct, CRUD, tim, sap, luu, menu, thong ke // Tat ca trong mot file. Khong kiem thu duoc rieng phan nao, // va sua mot cho phai doc ca file.
Chia module
sinh_vien.h/.c // struct va cac thao tac tren mot sinh vien danh_sach.h/.c // mang dong, them, xoa, tim, sap luu_tru.h/.c // luu/tai nhi phan va CSV thong_ke.h/.c // cac ham thong ke menu.c // giao dien dong lenh main.c // < 30 dong: khoi tao, goi menu, don dep // Moi module kiem thu duoc rieng. Xem Bai 56.3 ve chia module.

Tóm tắt

  • Mảng động tăng gấp đôi cho danh sách không giới hạn; dùng biến tạm cho realloc và chỉ số thay vì con trỏ.
  • Không bao giờ trừ để so sánh; dùng so sánh ba chiều để tránh tràn và mất phần lẻ.
  • Sắp nhiều tiêu chí: so tiêu chí chính, nếu bằng thì so tiêu chí phụ.
  • Ghi tệp an toàn: ghi tạm, kiểm tra, đổi tên nguyên tử, để mất điện không làm hỏng dữ liệu cũ.
  • Chia thành nhiều module có Makefile; chạy sạch dưới Valgrind và ASan.