Nội dung
Cách đơn giản nhất để tiếp cận về kiểu cấu trúc là xét ví dụ sinh viên.
Một lớp có 100 sinh viên mỗi sinh viên gồm họ tên và mã sinh viên. Hãy
nhập dữ liệu cho lớp đó. Hehe. Bạn nghĩ đến cách dùng 2 mảng: 1 mảng lưu
tên, 1 mảng lưu mã sinh viên đúng không. Đúng, cách đó không sai… nhưng
hãy xem yêu cầu tiếp theo… Thi học kỳ xong, hãy nhập điểm cho từng sinh
viên, mỗi sinh viên gồm 10 môn (Toán, Tin, Hóa, Vật lý,…). Hehe. Bạn
thấy sao nào… Dùng 12 mảng chăng… ồ không nên, hãy dùng kiểu cấu trúc.
Với kiểu cấu trúc chỉ cần 1 mảng mà thôi.
1. Kiểu cấu trúc
Đối với mảng, chỉ có thể lưu nhiều thông
tin có cùng kiểu dữ liệu. Nhưng với structure ta có thể lưu thông tin
có nhiều kiểu dữ liệu khác nhau.
VD mở đầu
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| #include #include // khai bao struct struct sinhvien { char MSV[20]; // ma sinh vien char hoten[30]; // ho ten sinh vien double toan, tin, anh; // cac diem toan, tin, anh }; /* Hay thay tat ca __fpurge(stdin); thanh fflush(stdin) khi ban lam tren windowns*/ int main() { /* khai bao 2 bien sv1, sv2 va 1 mang * CNPMK10A gom 100 sinh vien */ struct sinhvien sv1, sv2, CNPMK10A[100]; printf ( "Nhap du lieu cho sv1:\n" ); printf ( "MSV: " ); __fpurge(stdin); gets (sv1.MSV); printf ( "Ho ten: " ); __fpurge(stdin); gets (sv1.hoten); printf ( "Diem toan, tin, anh: " ); __fpurge(stdin); scanf ( "%lf %lf %lf" , &sv1.toan, &sv1.tin, &sv1.anh); printf ( "Nhap du lieu cho sv2:\n" ); printf ( "MSV: " ); __fpurge(stdin); gets (sv2.MSV); printf ( "Ho ten: " ); __fpurge(stdin); gets (sv2.hoten); printf ( "Diem toan, tin, anh: " ); __fpurge(stdin); scanf ( "%lf %lf %lf" , &sv2.toan, &sv2.tin, &sv2.anh); printf ( "\n --------- Thong tin sinh vien -----\n" ); printf ( "%-20s %-30s %-7s %-7s %-7s\n" , "MSV" , "Ho ten" , "Toan" , "Tin" , "Anh" ); printf ( "%-20s %-30s %-7.2lf %-7.2lf %-7.2lf\n" , sv1.MSV, sv1.hoten, sv1.toan, sv1.tin, sv1.anh); printf ( "%-20s %-30s %-7.2lf %-7.2lf %-7.2lf\n" , sv2.MSV, sv2.hoten, sv2.toan, sv2.tin, sv2.anh); return 0; } |
Kết quả:
01
02
03
04
05
06
07
08
09
10
11
12
13
| Nhap du lieu cho sv1: MSV: DTC1 Ho ten: Pham Thi Ha Diem toan, tin, anh: 9 9 8 Nhap du lieu cho sv2: MSV: DTC2 Ho ten: Nguyen Van Quan Diem toan, tin, anh: 9 9 8 --------- Thong tin sinh vien ----- MSV Ho ten Toan Tin Anh DTC1 Pham Thi Ha 9.00 9.00 8.00 DTC2 Nguyen Van Quan 9.00 9.00 8.00 |
Ở VD mở đầu này, chúng ta có rất nhiều điểu phải bàn…
a. Xây dựng kiểu cấu trúc, khai báo biến cấu trúc
Như VD trên, để xây dựng 1 kiểu cấu trúc ta thực hiện theo cú pháp:
1
2
3
4
| struct tên _kiểu_cấu_trúc { Khai báo các thành phần của kiểu; }; |
Sau khi có kiểu cấu trúc rồi thì cái
kiểu đó nó tương tự như 1 kiểu bình thường (int, float, char,…) và ta
chỉ việc khai báo biến nữa là xong. Tuy nhiên khai báo biến thì cần có
thêm từ khóa struct ở trước: (Đối với C++ thì không cần).
struct tên_kiểu_cấu_trúc tên_biến_cấu trúc;
Ngoài ta chúng ta còn một số cách xây dựng kiểu cấu trúc và khai báo biến cấu trúc như sau:
struct tên_kiểu_cấu_trúc tên_biến_cấu trúc;
Ngoài ta chúng ta còn một số cách xây dựng kiểu cấu trúc và khai báo biến cấu trúc như sau:
1
2
3
4
| struct tên _kiểu_cấu_trúc { Khai báo các thành phần của kiểu; } danh sách các biến thuộc kiểu cấu trúc ; |
Hoặc
1
2
3
4
| struct { Khai báo các thành phần của kiểu; } danh sách các biến thuộc kiểu cấu trúc ; |
Với cách khai báo này, ta bắt buộc phải
khai báo các biến cấu trúc ở ngay sau cấu trúc vì không có tên kiểu cấu
trúc để cho ta khai báo ở các vị trí khác nữa.
Trong phần này ta cần đề cập đến 1 từ khóa quan trọng nữa, đó là typedef. Từ khóa này dùng để định nghĩa 1 kiểu dữ liệu mới.
Trong phần này ta cần đề cập đến 1 từ khóa quan trọng nữa, đó là typedef. Từ khóa này dùng để định nghĩa 1 kiểu dữ liệu mới.
1
2
3
4
5
| typedef struct sinhvien { char MSV[20]; // ma sinh vien char hoten[30]; // ho ten sinh vien double toan, tin, anh; // cac diem toan, tin, anh } kieuSinhVien; |
Khi này ta có kieuSinhVien
là 1 kiểu dữ liệu (như int, double, …) và ta có thể khai báo các biến
cấu trúc thông qua nó. Trong này có vài điều các bạn cần chú ý:
- Với sinhvien (kiểu cấu trúc được đặt sau từ khóa struct) thì khi khai báo biến thuộc kiểu này ta vẫn phải có từ struct ở trước nó. (VD: sinhvien svA; -> Sai còn struct sinhvien svA; -> đúng), (chú ý trong C++ thì không cần).
- Với kieuSinhVIen thì khi khai báo biến thuộc kiểu này ta không được có từ struct ở trước nó. (VD: struct kieuSinhVIen svA; -> sai, kieuSinhVIen svA; -> đúng).
Ngoài ra ta còn có thể khai báo kiểu cấu
trúc lồng nhau: VD như trong 1 sinh viên có ngày sinh, trong ngày sinh
lại có ngày, tháng, năm sinh.
01
02
03
04
05
06
07
08
09
10
| struct ngaysinh { int ngay, thang, nam; } typedef struct sinhvien { char MSV[20]; // ma sinh vien char hoten[30]; // ho ten sinh vien double toan, tin, anh; // cac diem toan, tin, anh struct ngaysinh ns; } kieuSinhVien; |
Hoặc ta khai báo ngay trong cấu trúc:
1
2
3
4
5
6
7
8
| typedef struct sinhvien { char MSV[20]; // ma sinh vien char hoten[30]; // ho ten sinh vien double toan, tin, anh; // cac diem toan, tin, anh struct ngaysinh { int ngay, thang, nam; } ns; } kieuSinhVien; |
b. Truy cập đên các thành phần của cấu trúc
Để truy nhập đến thành phần của cấu trúc ta sử dụng toán tử chấm (.).
Tên_cấu_trúc . tên_thành_phần;
Như VD trên ta truy cập như sau:
sv1.hoten; sv1.toan; // truy xuất tới họ tên, điểm toán
sv1.ns.ngay; sv1.ns.thang; // truy xuất tới ngày sinh và tháng sinh.
…
Khi đã truy xuất được tới các thành phần của cấu trúc thì mỗi thành phần đó là 1 biến bình thường và ta gán giá trị hoặc nhập xuất giá trị cho chúng như cách bình thường mà chúng ta vẫn làm.
Ngoài ra nếu thành phần nào đó dài dòng thì ta có thể tránh việc dài dòng này bằng cách sử dụng từ khóa define.
VD thay vì viết:
Tên_cấu_trúc . tên_thành_phần;
Như VD trên ta truy cập như sau:
sv1.hoten; sv1.toan; // truy xuất tới họ tên, điểm toán
sv1.ns.ngay; sv1.ns.thang; // truy xuất tới ngày sinh và tháng sinh.
…
Khi đã truy xuất được tới các thành phần của cấu trúc thì mỗi thành phần đó là 1 biến bình thường và ta gán giá trị hoặc nhập xuất giá trị cho chúng như cách bình thường mà chúng ta vẫn làm.
Ngoài ra nếu thành phần nào đó dài dòng thì ta có thể tránh việc dài dòng này bằng cách sử dụng từ khóa define.
VD thay vì viết:
1
2
| sv1.ns.thang; sv1.ns.nam; |
Ta viết:
1
2
3
| #define p sv1.ns p.thang; p.nam; |
c. Gán các biến có cùng kiểu cấu trúc
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| #include #include struct sinhvien { char MSV[20]; // ma sinh vien char hoten[30]; // ho ten sinh vien double toan, tin, anh; // cac diem toan, tin, anh }; /* Hay thay tat ca __fpurge(stdin); thanh fflush(stdin) khi ban lam tren windowns*/ int main() { /* khai bao 2 bien sv1, sv2 va 1 mang * CNPMK10A gom 100 sinh vien */ struct sinhvien sv1, sv2, CNPMK10A[100]; printf ( "Nhap du lieu cho sv1:\n" ); printf ( "MSV: " ); __fpurge(stdin); gets (sv1.MSV); printf ( "Ho ten: " ); __fpurge(stdin); gets (sv1.hoten); printf ( "Diem toan, tin, anh: " ); __fpurge(stdin); scanf ( "%lf %lf %lf" , &sv1.toan, &sv1.tin, &sv1.anh); sv2 = sv1; // gan gia tri cua sv1 cho sv2 printf ( "\n --------- Thong tin sinh vien -----\n" ); printf ( "%-20s %-30s %-7s %-7s %-7s\n" , "MSV" , "Ho ten" , "Toan" , "Tin" , "Anh" ); printf ( "%-20s %-30s %-7.2lf %-7.2lf %-7.2lf\n" , sv2.MSV, sv2.hoten, sv2.toan, sv2.tin, sv2.anh); return 0; } |
Sau khi gán sv2 = sv1 thì mọi thông tin của sv1 có thì sv2 cũng có.
Ngoài ra ta còn có thể gán giá trị khởi đầu cho cấu trúc.
struct sinhvien sv1 = {“ABC”, “Nguyen Van Quan”, 9, 9, 8, {4, 5, 1992}};
Khi đó ta có các dữ liệu ban đầu của sv1 là:
Ngoài ra ta còn có thể gán giá trị khởi đầu cho cấu trúc.
struct sinhvien sv1 = {“ABC”, “Nguyen Van Quan”, 9, 9, 8, {4, 5, 1992}};
Khi đó ta có các dữ liệu ban đầu của sv1 là:
1
2
3
4
5
6
| MSV: ABC hoten: Nguyen Van Quan toan: 9 tin: 9 anh: 8 ngày sinh: 4/5/1992. |
2. Mảng cấu trúc
Bên trên ta đã tìm hiểu cơ bản về kiểu
cấu trúc và một vài ví dụ về cấu trúc sinhvien. Bây giờ ta tìm hiểu cách
biểu diễn 1 mảng 50 sinh viên của 1 lớp học có kiểu cấu trúc như trên.
Ta xét VD:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| #include #include struct sinhvien { char MSV[20]; // ma sinh vien char hoten[30]; // ho ten sinh vien double diemTB; // diem trung binh struct ngaysinh { int ngay, thang, nam; } ns; }; void fflush_stdin(){ __fpurge(stdin); } int main() { int n = 2, i; struct sinhvien CNPMK10A[n]; for (i = 0; i < n; i++) { #define sv CNPMK10A[i] printf ( "Nhap du lieu cho sinh vien thu %d:\n" , i + 1); printf ( "MSV: " ); fflush_stdin(); gets (sv.MSV); printf ( "Ho ten: " ); fflush_stdin(); gets (sv.hoten); printf ( "Diem TB: " ); fflush_stdin(); scanf ( "%lf" , &sv.diemTB); printf ( "Ngay sinh: " ); scanf ( "%d/%d/%d" , &sv.ns.ngay, &sv.ns.thang, &sv.ns.nam); } printf ( "\n --------- Thong tin sinh vien -----\n" ); printf ( "%-20s %-30s %-7s %-10s\n" , "MSV" , "Ho ten" , "Diem Tb" , "Ngay sinh" ); for (i = 0; i < n; i++) { #define sv CNPMK10A[i] printf ( "%-20s %-30s %-7.2lf %02d/%02d/%4d\n" , sv.MSV, sv.hoten, sv.diemTB, sv.ns.ngay, sv.ns.thang, sv.ns.nam); } return 0; } |
Kết quả:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
| Nhap du lieu cho sinh vien thu 1: MSV: DTC1 Ho ten: Pham Thi Ha Diem TB: 9.2 Ngay sinh: 21/01/1993 Nhap du lieu cho sinh vien thu 2: MSV: DTC2 Ho ten: Nguyen Van Quan Diem TB: 9.2 Ngay sinh: 31/12/1992 --------- Thong tin sinh vien ----- MSV Ho ten Diem Tb Ngay sinh DTC1 Pham Thi Ha 9.20 21/01/1993 DTC2 Nguyen Van Quan 9.20 31/12/1992 |
3. Con trỏ cấu trúc
Xét VD về con trỏ cấu trúc:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| #include #include struct sinhvien { char MSV[20]; // ma sinh vien char hoten[30]; // ho ten sinh vien double diemTB; // diem trung binh struct ngaysinh { int ngay, thang, nam; } ns; }; // fflush(stdin) tren windows void fflush_stdin(){ __fpurge(stdin); } int main() { int n = 2, i; // cap phat bo nho struct sinhvien *CNPMK10A = ( struct sinhvien*) malloc (n * sizeof ( struct sinhvien)); for (i = 0; i < n; i++) { printf ( "Nhap du lieu cho sinh vien thu %d:\n" , i + 1); printf ( "MSV: " ); fflush_stdin(); gets (CNPMK10A[i].MSV); printf ( "Ho ten: " ); fflush_stdin(); gets (CNPMK10A[i].hoten); printf ( "Diem TB: " ); fflush_stdin(); scanf ( "%lf" , &(CNPMK10A+i)->diemTB); printf ( "Ngay sinh: " ); scanf ( "%d/%d/%d" , &(CNPMK10A+i)->ns.ngay, &(CNPMK10A+i)->ns.thang, &(CNPMK10A+i)->ns.nam); } printf ( "\n --------- Thong tin sinh vien -----\n" ); printf ( "%-20s %-30s %-7s %-10s\n" , "MSV" , "Ho ten" , "Diem Tb" , "Ngay sinh" ); for (i = 0; i < n; i++) { #define ns CNPMK10A[i].ns printf ( "%-20s %-30s %-7.2lf %02d/%02d/%4d\n" , CNPMK10A[i].MSV, (*(CNPMK10A+i)).hoten, (CNPMK10A+i)->diemTB, ns.ngay, ns.thang, ns.nam); } return 0; } |
Truy cập các thành phần cấu trúc
Để truy cập để lấy dữ liệu các thành phần của con trỏ cấu trúc ta có 3 cách sau:
Để truy cập để lấy dữ liệu các thành phần của con trỏ cấu trúc ta có 3 cách sau:
- Cách 1: CNPMK10A[i].diemTB;
- Cách 2: (*(CNPMK10A+i)).diemTB;
- Cách 3: (CNPMK10A+i) ->diemTB;
Cả 3 cách trên đều truy cập tới DTB.
Để lấy địa chỉ ta cũng có 2 cách:
- Cách 1: &CNPMK10A[i].DTB;
- Cách 2: &(CNPMK10A+i)->diemTB
0 nhận xét:
Đăng nhận xét