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ăng | Kỹ thuật | Chương |
|---|---|---|
| Danh sách không giới hạn | Mảng động realloc gấp đôi | Phần 6 |
| Thêm, sửa, xoá, xem | CRUD với kiểm tra hợp lệ | Phần 3 tới 5 |
| Tìm theo MSSV | Tìm kiếm nhị phân | Phần 8 |
| Tìm theo tên | Chuỗi con, không phân biệt hoa thường | Phần 4 |
| Sắp xếp nhiều tiêu chí | qsort với nhiều hàm so sánh | Phần 8 |
| Lưu và tải | Tệp nhị phân và CSV | Phầ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
Ghi vào tệp TẠM
Không ghi thẳng vàodulieu.dat. Ghi vàodulieu.dat.tmptrước.Kiểm tra ghi thành công
Kiểm tra mọifwritetrả về đúng số phần tử, vàfclosetrả 0. Nếu lỗi, xoá tệp tạm và báo.Đổ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ử
- Mảng động tăng gấp đôi, không giới hạn cứng.
- 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ệ.
- 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.
- Sắp xếp
qsortnhiều tiêu chí, cả tăng và giảm. - Lưu và tải tệp nhị phân, cộng xuất và nhập CSV.
- Thống kê: theo lớp, theo xếp loại, top N, danh sách cảnh báo.
- Chia thành ít nhất năm tệp
.cvà.hcộng Makefile. - Ghi tệp an toàn bằng ghi tạm rồi đổi tên.
- 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
reallocvà 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.