Trong bài hướng dẫn này, bạn sẽ cùng Lập trình không khó tìm hiểu về kiểu struct trong C (kiểu cấu trúc). Bạn sẽ học cách định nghĩa và sử dụng kiểu cấu trúc với sự đi kèm của các ví dụ. Nếu dịch từ tiếng anh ra thì nghĩa của nó là kiểu cấu trúc, tuy nhiên chúng ta vẫn thường hay gọi nó là kiểu struct. Nhưng mục tiêu sau cùng của chúng ta là hiểu và biết cách sử dụng struct trong C, cùng bắt đầu nào…
Cách định nghĩa struct trong C
Trước khi chúng ta có thể khai báo biến với struct, bạn cần định nghĩa nó – Đó cũng là lý do tại sao struct được gọi là kiểu dữ liệu người dùng định nghĩa.
Khi nào chúng ta cần phải tự định nghĩa 1 kiểu cấu trúc? Khi bạn cần lưu trữ một đối tượng có nhiều thuộc tính. Ví dụ, đối tượng SinhVien có các thuộc tính (Mã sinh viên, họ, tên, giới tính, quê quán,…) hay đối tượng LopHoc có các thuộc tính (Mã lớp, tên lớp, giáo viên chủ nhiệm, sĩ số,…). Khi đó chúng ta nên dùng struct để quản lý chương trình.
Cú pháp định nghĩa struct trong C
struct structureName { dataType member1; dataType member2; ... };
Dưới đây là 1 ví dụ:
struct SinhVien { int maSV; char ho[20]; char ten[20]; bool gioiTinh; char queQuan[100]; };
Như vậy, kiểu dữ liệu SinhVien
đã được định nghĩa. Từ đây chúng ta có thể khai báo các biến với kiểu dữ liệu này.
Cách khai báo biến kiểu struct trong C
Việc khai báo biến với struct cũng giống như cách khai báo biến thông thường, trong đó kiểu dữ liệu là kiểu struct trong C mà bạn vừa định nghĩa. Xem ví dụ dưới đây:
struct SinhVien { int maSV; char ho[20]; char ten[20]; bool gioiTinh; char queQuan[100]; }; int main(){ // Khai báo 2 biến sv1 và sv2 có kiểu SinhVien SinhVien sv1, sv2; // Ta nên thêm từ khóa struct ở đầu, // để phân biệt được biến này là biến của kiểu dữ liệu tự định nghĩa struct SinhVien sv3, sv4; // Khai báo mảng struct SinhVien sv[100]; }
Truy xuất các thuộc tính của struct
Chúng ta có 2 toán tử dùng để truy xuất tới các biến thành viên của kiểu struct trong C.
- Sử dụng `.` => Toán tử truy xuất tới thành viên khi khai báo biến bình thương.
- Sử dụng `->` => Toán tử truy xuất tới thành viên khi biến là con trỏ.
Giả sử trong ví dụ trên, bạn muốn truy xuất gioiTinh
của đối tượng sinh viên, bạn làm như sau:
SinhVien sv; // to do printf("Gioi tinh: %s", sv.gioiTinh);
Từ khóa typedef
Bạn có thể sử dụng từ khóa typedef
để tạo ra một tên thay thế cho kiểu dữ liệu đã có. Nó thường được sử dụng kiểu struct để đơn giản hóa cú pháp khai báo biến. Nhưng nó cũng có thể sử dụng với các kiểu dữ liệu nguyên thủy nhé.
struct Distance{ int feet; float inch; }; int main() { structure Distance d1, d2; }
Code trên tương đương với:
typedef struct SinhVien{ int feet; float inch; } distances; int main() { distances d1, d2; }
Hoặc:
struct PhanSo{ int tu; int mau; }; typedef struct PhanSo PS;
Hoặc bạn có thể dùng với kiểu nguyên thủy như sau:
typedef int U_INT8; // Khai báo biến kiểu int U_INT8 value;
Tuy nhiên, nếu không có nhu cầu thực tế thì ta cũng không nên bày vẽ làm gì. Trong một số code đặc thù ta muốn có quy chuẩn riêng thì nên dùng.
Cấu trúc struct lồng nhau
Giả sử bạn muốn xây dựng kiểu dữ liệu để lưu trữ đối tượng Tam giác, khi đó chúng ta có thể xây dựng struct mô tả tọa độ của 1 điểm, khi đó đối tượng tam giác sẽ là 3 đối tượng điểm. Cụ thể:
struct Point{ int x; // hoành độ int y; // tung độ }; struct Triangle{ Point a; // đỉnh thứ 1 Point b; // đỉnh thứ 2 Point c; // đỉnh thứ 3 } int main(){ Triangle tg; // truy xuất hoành độ của điểm thứ nhất tg.a.x = 5; }
Sau đây là các ví dụ sử dụng kiểu cấu trúc struct trong C vào các bài tập thực tế. Các bạn tham khảo và chạy thử cũng như thử sửa đổi các code mẫu này để hiểu hơn về struct nhé.
Chương trình cộng trừ nhân chia phân số trong C
Code này mình giả sử các bạn nhập mẫu số cho phân số khác 0 nhé. Bạn có thể kiểm tra bổ sung thêm các tùy chọn/ chức năng để code tối ưu hơn.
Phần thuật toán tìm ước chung lớn nhất, bạn có thể xem tại bài tìm ước chung lớn nhất.
#include <stdio.h> #include <string.h> #include <math.h> int UCLN(int a, int b) { a = abs(a); b = abs(b); while (a * b != 0) { if (a > b) a %= b; else b %= a; } return a + b; } int BSCNN(int a, int b) { return a * b / UCLN(a, b); } typedef struct PhanSo { int tuso, mauso; } PS; PS rutGon(PS a) { PS c; c.tuso = a.tuso / UCLN(a.tuso, a.mauso); c.mauso = a.mauso / UCLN(a.tuso, a.mauso); return c; } PS cong(PS a, PS b) { PS c; c.tuso = a.tuso * b.mauso + a.mauso * b.tuso; c.mauso = a.mauso * b.mauso; c = rutGon(c); return c; } PS tru(PS a, PS b) { PS c; c.tuso = a.tuso * b.mauso - a.mauso * b.tuso; c.mauso = a.mauso * b.mauso; c = rutGon(c); return c; } PS nhan(PS a, PS b) { PS c; c.tuso = a.tuso * b.tuso; c.mauso = a.mauso * b.mauso; c = rutGon(c); return c; } PS chia(PS a, PS b) { PS c; c.tuso = a.tuso * b.mauso; c.mauso = a.mauso * b.tuso; c = rutGon(c); return c; } void print(PS a) { printf("%d/%d", a.tuso, a.mauso); } int main() { PS a, b, c; printf("nNhap phan so a : "); scanf("%d%d", &a.tuso, &a.mauso); printf("nNhap phan so b : "); scanf("%d%d", &b.tuso, &b.mauso); printf("nToi gian a ta duoc : "); a = rutGon(a); print(a); printf("nToi gian b ta duoc : "); b = rutGon(b); print(b); printf("nTong cua hai phan so = "); c = cong(a, b); print(c); printf("nHieu cua hai phan so = "); c = tru(a, b); print(c); printf("nTich cua hai phan so = "); c = nhan(a, b); print(c); printf("nThuong cua hai phan so = "); c = chia(a, b); print(c); }
Kết quả chạy:
Nhap phan so a : 3 4 Nhap phan so b : 2 3 Toi gian a ta duoc : 3/4 Toi gian b ta duoc : 2/3 Tong cua hai phan so = 17/12 Hieu cua hai phan so = 1/12 Tich cua hai phan so = 1/2 Thuong cua hai phan so = 9/8
Struct và con trỏ
Tương tự như khai báo con trỏ với các kiểu dữ liệu có sẵn trong C. Chúng ta cũng có thể khai báo biến con trỏ, cấp phát động cho biến con trỏ kiểu struct.
Sau đây là cách chúng ta khai báo biến con trỏ kiểu struct trong C:
struct name { member1; member2; . . }; int main() { struct name *ptr, Harry; }
Khi đó ptr
là con trỏ kiểu name
, còn Harry
là biến kiểu name
.
Để truy cập vào các biến thành viên sử dụng biến con trỏ của struct trong C, bạn dùng ->
, ví dụ:
#include <stdio.h> struct person { int age; float weight; }; int main() { struct person *personPtr, person1; personPtr = &person1; printf("Enter age: "); scanf("%d", &personPtr->age); printf("Enter weight: "); scanf("%f", &personPtr->weight); printf("Displaying:n"); printf("Age: %dn", personPtr->age); printf("weight: %f", personPtr->weight); return 0; }
Trong ví dụ này, địa chỉ của biến person1
được lưu giữ bởi con trỏ personPtr
. Và bạn có thể thao tác với biến con trỏ giống như chúng ta đã học ở bài Con trỏ trong C.
Ta có:
personPtr->age
cho kết quả giống với(*personPtr).age
personPtr->weight
cho kết quả giống với(*personPtr).weight
Cấp phát bộ nhớ động
Trước khi bạn đọc phần này, mình hi vọng các bạn đã có kiến thức về cấp phát động trong C.
Đôi khi, số lượng biến struct trong C mà chúng ta cần có thể lớn. Khi đó có thể bạn sẽ cần tới cấp phát động trong quá trình chương trình thực thi. Dưới đây là cách để cấp phát bộ nhớ động với kiểu cấu trúc:
#include <stdio.h> #include <stdlib.h> struct person { int age; float weight; char name[30]; }; int main() { struct person *ptr; int i, n; printf("Enter the number of persons: "); scanf("%d", &n); // allocating memory for n numbers of struct person ptr = (struct person*) malloc(n * sizeof(struct person)); for(i = 0; i < n; ++i) { printf("Enter first name and age respectively: "); // To access members of 1st struct person, // ptr->name and ptr->age is used // To access members of 2nd struct person, // (ptr+1)->name and (ptr+1)->age is used scanf("%s %d", (ptr+i)->name, &(ptr+i)->age); } printf("Displaying Information:n"); for(i = 0; i < n; ++i) printf("Name: %stAge: %dn", (ptr+i)->name, (ptr+i)->age); return 0; }
Kết quả khi chạy chương trình:
Enter the number of persons: 2 Enter first name and age respectively: Harry 24 Enter first name and age respectively: Gary 32 Displaying Information: Name: Harry Age: 24 Name: Gary Age: 32
Trong ví dụ trên, sau khi người dùng nhập số lượng n
thì ta mới tiến hành cấp phát đúng n
ô nhớ sử dụng dòng lệnh này:
ptr = (struct person*) malloc(n * sizeof(struct person));
Và sau đó, ta dùng con trỏ ptr
để truy cập vào các thành viên của person
.
Hoặc bài tập cấp phát động cho kiểu cấu trúc sinh viên dưới đây, ta thực hiện nhập, xuất và sắp xếp danh sách sinh viên theo điểm sử dụng cấp phát động cho con trỏ trong C++ (new và delete):
#include<iostream> #include<stdio.h> #include<math.h> #include<string.h> using namespace std; struct NGAYTHANG { int ngay; int thang; int nam; }; struct SV { char masv[12]; char hodem[30]; char ten[10]; NGAYTHANG ngsinh; char gioitinh[4]; char hokhau[20]; float diem; }; void Nhapsv(SV * sv) { cout << "tMa sv: "; cin >> sv->masv; cout << "tHo dem: "; cin.ignore(); fgets(sv->hodem, sizeof(sv->hodem), stdin); sv->hodem[strlen(sv->hodem) - 1] = ' '; cout << "tTen: "; cin.ignore(); fgets(sv->ten, sizeof(sv->ten), stdin); cout << "tNgay sinh: "; cin >> sv->ngsinh.ngay; cout << "tThang sinh: "; cin >> sv->ngsinh.thang; cout << "tNam sinh: "; cin >> sv->ngsinh.nam; cout << "tGioi tinh: "; cin.ignore(); fgets(sv->gioitinh, sizeof(sv->gioitinh), stdin); sv->gioitinh[strlen(sv->gioitinh) - 1] = ' '; cout << "tHo khau: "; cin.ignore(); fgets(sv->hokhau, sizeof(sv->hokhau), stdin); sv->hokhau[strlen(sv->hokhau) - 1] = ' '; cout << "tDiem: "; cin >> sv->diem; } void Hienthisv(SV * sv) { cout << sv->masv; cout << "t" << sv->hodem; cout << " " << sv->ten; cout << "t" << sv->ngsinh.ngay; cout << "-" << sv->ngsinh.thang; cout << "-" << sv->ngsinh.nam; cout << "t" << sv->gioitinh; cout << "t" << sv->hokhau; cout << "t" << sv->diem; } void Nhapds(SV *p, int n) { for (int i = 0; i < n; i++) { cout << "Nhap thong tin cua sv thu " << i + 1 << " :" << endl; Nhapsv(p + i); } } void Hienthids(SV *p, int n) { for (int i = 0; i < n; i++) { Hienthisv(p + i); cout << "n"; } } void Sapxep(SV *p, int n) { for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) if ((p + i)->diem > (p + j)->diem) { SV tmp = * (p + j); *(p + j) = * (p + i); *(p + i) = tmp; } } } int main() { SV *p; int n; do { cout << "Nhap vao so sv: "; cin >> n; } while (n < 0 || n > 10); p = new SV[n]; cout << "Nhap vao thong tin " << n << " sv: " << endl; Nhapds(p, n); cout << "Hien thi thong tin vua nhap: " << endl; Hienthids(p, n); cout << "nDanh sach sau khi sap xep la: " << endl; Sapxep(p, n); Hienthids(p, n); delete p; return 0; }
Kết quả chạy:
Nhap vao so sv: 3 Nhap vao thong tin 3 sv: Nhap thong tin cua sv thu 1 : Ma sv: 5 Ho dem: 5 Ten: 5 Ngay sinh: 5 Thang sinh: 5 Nam sinh: 5 Gioi tinh: 5 Ho khau: 5 Diem: 5 Nhap thong tin cua sv thu 2 : Ma sv: 3 Ho dem: 3 Ten: 3 Ngay sinh: 3 Thang sinh: 3 Nam sinh: 3 Gioi tinh: 3 Ho khau: 3 Diem: 3 Nhap thong tin cua sv thu 3 : Ma sv: 9 Ho dem: 9 Ten: 9 Ngay sinh: 9 Thang sinh: 9 Nam sinh: 9 Gioi tinh: 9 Ho khau: 9 Diem: 9 Hien thi thong tin vua nhap: 5 5 5-5-5 5 5 3 3 3-3-3 3 3 9 9 9-9-9 9 9 Danh sach sau khi sap xep la: 3 3 3-3-3 3 3 5 5 5-5-5 5 5 9 9 9-9-9 9 9
Ngoài ra, mình cũng có một bài hướng dẫn chi tiết và nâng cao hơn: Bài tập quản lý sinh viên sử dụng struct trong C. Các bạn tiếp tục tham khảo bài viết này nhé!
Để lại một bình luận