VI - Dữ liệu cấu trúc trong C
A. Giới thiệu:
Dữ liệu cấu trúc (structure) trong C cho phép người lập trình tạo ra các kiểu dữ liệu mới, tổ chức và lưu trữ nhiều loại dữ liệu khác nhau dưới một tên gọi duy nhất. Điều này giúp cho việc quản lý dữ liệu trở nên dễ dàng hơn và giảm thiểu lỗi khi truy xuất dữ liệu.
Cấu trúc (structure) là một kiểu dữ liệu trong C, được tạo ra bằng cách kết hợp các kiểu dữ liệu khác nhau lại với nhau. Cấu trúc cho phép tạo ra các đối tượng dữ liệu tự định nghĩa.
Cấu trúc được khai báo bằng từ khóa "struct", theo sau là tên của cấu trúc và danh sách các trường (field) được đặt trong ngoặc nhọn. Mỗi trường là một biến của một kiểu dữ liệu bất kỳ.
Cấu trúc trong C giúp cho việc quản lý dữ liệu trở nên dễ dàng hơn bằng cách tạo ra các đối tượng dữ liệu tự định nghĩa. Có thể sử dụng các phép toán trên cấu trúc, truy cập các trường của cấu trúc để thực hiện các thao tác trên dữ liệu.
Cấu trúc trong C có thể được sử dụng trong các trường hợp như:
Quản lý các đối tượng phức tạp và có cấu trúc, ví dụ như đối tượng hình học, đối tượng xe, đối tượng người, v.v.
Lưu trữ dữ liệu liên quan đến một thực thể cụ thể, ví dụ như lưu trữ thông tin của một học sinh, một nhân viên, v.v.
Truyền dữ liệu giữa các hàm một cách dễ dàng và hiệu quả hơn.
Cấu trúc trong C cũng có thể được sử dụng kết hợp với con trỏ để tạo ra các đối tượng dữ liệu động.
Cấu trúc cũng có thể được sử dụng để tạo ra các kiểu dữ liệu mới. Điều này cho phép người lập trình tạo ra các kiểu dữ liệu được tùy chỉnh cho nhu cầu cụ thể của mình.
Cấu trúc cũng có thể được kết hợp với các khái niệm khác như union, enum, để tạo ra các kiểu dữ liệu phức tạp hơn.
B. Khai báo dữ liệu cấu trúc trong C
Dữ liệu cấu trúc trong C được khai báo bằng từ khóa "struct
". Cú pháp khai báo dữ liệu cấu trúc như sau:
struct [tên cấu trúc] {
[kiểu dữ liệu] [tên thành phần 1];
[kiểu dữ liệu] [tên thành phần 2];
...
[kiểu dữ liệu] [tên thành phần n];
};
Ví dụ, ta có thể khai báo một cấu trúc tên là "sinh_vien
" để lưu trữ thông tin của một sinh viên như sau:
struct sinh_vien {
char ho_ten[50];
int nam_sinh;
float diem_tb;
};
C. Truy cập đến các thành phần của dữ liệu cấu trúc trong C
Các thành phần của dữ liệu cấu trúc có thể được truy cập bằng toán tử ".". Ví dụ, để truy cập đến thành phần "ho_ten" của cấu trúc "sinh_vien", ta có thể sử dụng câu lệnh:
sinh_vien sv;
strcpy(sv.ho_ten, "Nguyen Van A");
Ngoài ra, để truy cập đến thành phần của dữ liệu cấu trúc thông qua con trỏ, ta sử dụng toán tử "->
". Ví dụ, nếu ta khai báo một con trỏ "ptr_sv
" trỏ đến cấu trúc "sinh_vien
", ta có thể truy cập đến thành phần "nam_sinh
" của cấu trúc thông qua câu lệnh:
sinh_vien *ptr_sv;
ptr_sv = &sv;
ptr_sv->nam_sinh = 2000;
D. Sử dụng dữ liệu cấu trúc trong C
Dữ liệu cấu trúc trong C có thể được sử dụng để lưu trữ thông tin phức tạp hơn so với kiểu dữ liệu cơ bản. Ví dụ, ta có thể sử dụng dữ liệu cấu trúc để lưu trữ thông tin của nhiều sinh viên trong một mảng, như sau:
struct sinh_vien ds_sinh_vien[50];
Sau đó, ta có thể truy cập đến các thành phần của mỗi sinh viên trong mảng thông qua toán tử ".
" hoặc "->
".
E. Cấu trúc dữ liệu lồng nhau
Trong C, bạn có thể khai báo cấu trúc dữ liệu lồng nhau. Điều này cho phép bạn lưu trữ các cấu trúc dữ liệu khác nhau trong một cấu trúc dữ liệu.
Ví dụ:
struct DiaChi {
char so_nha[50];
char ten_duong[50];
char thanh_pho[50];
};
struct SinhVien {
int maSV;
char ten[50];
float diemTB;
struct DiaChi dia_chi;
};
Trong ví dụ này, SinhVien
là cấu trúc dữ liệu lồng DiaChi
F. Ứng dụng Danh sách liên kết
Danh sách liên kết con trỏ (linked list) là một cấu trúc dữ liệu phổ biến trong lập trình, được sử dụng để lưu trữ và quản lý các phần tử có thể thay đổi số lượng và có quan hệ liên kết với nhau. Trong C, linked list thường được triển khai bằng cách sử dụng con trỏ.
Ví dụ, ta có thể sử dụng danh sách liên kết con trỏ để lưu trữ thông tin về sinh viên, mỗi sinh viên là một struct gồm các thông tin như tên, mã số, điểm trung bình, v.v...
Cấu trúc của struct SinhVien:
typedef struct SinhVien {
char ten[50];
int maSV;
float diemTB;
struct SinhVien* next; // con trỏ đến phần tử tiếp theo trong danh sách liên kết
} SinhVien;
Ta sử dụng con trỏ next để liên kết các sinh viên với nhau trong danh sách liên kết.
Để thêm một sinh viên mới vào danh sách, ta cần cập nhật con trỏ next của sinh viên cuối cùng trong danh sách, trỏ đến sinh viên mới thêm vào.
Ví dụ hàm thêm sinh viên mới vào danh sách:
// head là con trỏ trỏ đến sinh viên đầu tiên trong danh sách
void themSinhVien(SinhVien** head, char ten[], int maSV, float diemTB) {
SinhVien* svMoi = (SinhVien*)malloc(sizeof(SinhVien)); // cấp phát bộ nhớ cho sinh viên mới
strcpy(svMoi->ten, ten);
svMoi->maSV = maSV;
svMoi->diemTB = diemTB;
svMoi->next = NULL; // khởi tạo con trỏ next của sinh viên mới là NULL
if (*head == NULL) { // danh sách rỗng
*head = svMoi;
}
else {
SinhVien* cur = *head;
while (cur->next != NULL) { // tìm sinh viên cuối cùng trong danh sách
cur = cur->next;
}
cur->next = svMoi; // cập nhật con trỏ next của sinh viên cuối cùng
}
}
Để tìm kiếm một sinh viên trong danh sách, ta cần duyệt từ đầu đến cuối danh sách, so sánh thông tin của mỗi sinh viên với thông tin cần tìm.
Ví dụ hàm tìm kiếm sinh viên theo mã số:
SinhVien* timSinhVien(SinhVien* head, int maSV) {
SinhVien* cur = head;
while (cur != NULL) { // duyệt danh sách
if (cur->maSV == maSV) { // nếu tìm thấy sinh viên có mã số cần tìm
return cur;
}
cur = cur->next;
}
return NULL; // không tìm thấy sinh viên có mã số cần tìm
}
Ví dụ về hàm sửa thông tin sinh viên theo mã sinh viên:
void suaSinhVien(SinhVien *head, int maSV, char hoTen[], float diemTB) {
SinhVien *p = timSinhVien(head, maSV); // tìm sinh viên cần sửa
if (p == NULL) {
printf("Khong tim thay sinh vien co ma so %d", maSV);
} else {
strcpy(p->hoTen, hoTen); // sửa họ tên sinh viên
p->diemTB = diemTB; // sửa điểm trung bình
printf("Da sua thong tin sinh vien co ma so %d\n", maSV);
}
}
Trong hàm trên, đầu vào là con trỏ head trỏ đến danh sách liên kết con trỏ của struct SinhVien, mã sinh viên cần sửa, họ tên mới và điểm trung bình mới của sinh viên. Hàm sử dụng hàm timSinhVien để tìm sinh viên cần sửa, nếu không tìm thấy thì in ra thông báo và kết thúc hàm. Nếu tìm thấy sinh viên cần sửa, thì sửa thông tin họ tên và điểm trung bình của sinh viên đó và in ra thông báo.
Kết luận :
Trong C, dữ liệu cấu trúc là một công cụ mạnh mẽ để định nghĩa và quản lý các dữ liệu có liên quan đến nhau. Cấu trúc cho phép tạo ra các đối tượng mới có thể lưu trữ nhiều loại dữ liệu khác nhau, giúp cho quản lý dữ liệu trở nên dễ dàng hơn.
Danh sách liên kết là một trong những cách phổ biến nhất để lưu trữ và quản lý dữ liệu trong C. Sử dụng con trỏ để liên kết các nút trong danh sách liên kết, ta có thể dễ dàng thêm, xóa hoặc sửa đổi dữ liệu trong danh sách. Ngoài ra, danh sách liên kết còn cho phép lưu trữ các đối tượng có kích thước khác nhau.
Tuy nhiên, để sử dụng hiệu quả dữ liệu cấu trúc và danh sách liên kết, cần phải hiểu rõ về các khái niệm như con trỏ, phép toán trên con trỏ, truy cập và thao tác với các phần tử trong mảng hay danh sách. Việc này đòi hỏi người lập trình phải có kiến thức vững chắc về ngôn ngữ C và kinh nghiệm thực tế.