Trong bài chỉ dẫn này, bạn sẽ cùng Lập trình không khó tìm tòi về kiểu struct trong C (kiểu cấu trúc). Bạn sẽ học cách khái niệm & sử dụng kiểu cấu trúc với sự đi kèm của các chẳng hạn. Nếu dịch từ tiếng anh ra thì nghĩa của nó là kiểu cấu trúc, ngoài ra tất cả 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 các bạn là hiểu & biết cách dùng struct trong C, cùng khởi đầu nào…
Cách khái niệm struct trong C
Trước khi tất cả chúng ta có thể khai báo biến với struct, bạn cần khái niệm nó – Đó cũng là nguyên nhân vì sao struct được gọi là kiểu dữ liệu người dùng khái niệm.
Bao giờ tất cả chúng ta cần phải tự khái niệm 1 kiểu cấu trúc? Khi bạn cần lưu trữ một đối tượng có nhiều tính chất. Chẳng hạn, đối tượng SinhVien có các tính chất (Mã sinh viên, họ, tên, giới tính, quê quán,…) hay đối tượng LopHoc có các tính chất (Mã lớp, tên lớp, thầy giáo chủ nhiệm, sĩ số,…). Khi đó tất cả chúng ta nên dùng struct để làm chủ chương trình.
Cú pháp khái niệm struct trong C
1
2
3
4
5
6
7
struct
structureName
{
dataType
member1
;
dataType
member2
;
.
.
.
}
;
Dưới đây là 1 chẳng hạn:
1
2
3
4
5
6
7
8
struct
SinhVien
{
int
maSV
;
char
ho
[
20
]
;
char
ten
[
20
]
;
bool
gioiTinh
;
char
queQuan
[
100
]
;
}
;
Như thế, kiểu dữ liệu SinhVien
đã được khái niệm. Từ đây tất cả 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 tương tự với 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 khái niệm. Xem chẳng hạn dưới đây:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct
SinhVien
{
int
maSV
;
char
ho
[
20
]
;
char
ten
[
20
]
;
bool
gioiTinh
;
char
queQuan
[
100
]
;
}
;
int
main
(
)
{
// Khai báo 2 biến sv1 & sv2 có kiểu SinhVien
SinhVien
sv1
,
sv2
;
// Ta nên thêm keyword struct ở đầu,
// để phân biệt được biến đó là biến của kiểu dữ liệu tự khái niệm
struct
SinhVien
sv3
,
sv4
;
// Khai báo mảng
struct
SinhVien
sv
[
100
]
;
}
Truy xuất các tính chất của struct
Tất cả chúng ta có 2 toán tử dùng để truy xuất tới các biến member của kiểu struct trong C.
- Sử dụng
.
=> Toán tử truy xuất tới member khi khai báo biến bình thương. - Sử dụng
->
=> Toán tử truy xuất tới member khi biến là con trỏ.
Giả sử trong chẳng hạn trên, bạn mong muốn truy xuất gioiTinh
của đối tượng sinh viên, bạn làm như sau:
1
2
3
4
SinhVien
sv
;
// to do
printf
(
“Gioi tinh: %s”
,
sv
.
gioiTinh
)
;
Keyword typedef
Bạn có thể sử dụng keyword typedef
để tạo nên một tên thay thế cho kiểu dữ liệu đã có. Nó hay được dùng kiểu struct để dễ dàng 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é.
1
2
3
4
5
6
7
8
9
struct
Distance
{
int
feet
;
float
inch
;
}
;
int
main
(
)
{
structure
Distance
d1
,
d2
;
}
Code trên tương tự với:
1
2
3
4
5
6
7
8
9
typedef
struct
SinhVien
{
int
feet
;
float
inch
;
}
distances
;
int
main
(
)
{
distances
d1
,
d2
;
}
Hoặc:
1
2
3
4
5
6
7
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:
1
2
3
4
5
typedef
int
U_INT8
;
// Khai báo biến kiểu int
U_INT8
value
;
Ngoài ra, còn nếu như không có nhu cầu thực tiễn thì ta cũng không nên bày vẽ làm gì. Trong một số code đặc trưng ta mong 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 mong muốn xây dựng kiểu dữ liệu để lưu trữ đối tượng Tam giác, khi đó tất cả chúng ta có thể xây dựng struct miêu tả tọa độ của một điểm, khi đó đối tượng tam giác sẽ là 3 đối tượng điểm. Rõ ràng:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct
Point
{
int
Ҳ
;
// hoành độ
int
y
;
// tung độ
}
;
struct
Triangle
{
Point
α
;
// đỉnh thứ 1
Point
ɓ
;
// đỉnh thứ 2
Point
ͼ
;
// đỉnh thứ 3
}
int
main
(
)
{
Triangle
tg
;
// truy xuất hoành độ của điểm đầu tiên
tg
.
α
.
Ҳ
=
5
;
}
Sau đây là các chẳng hạn sử dụng kiểu cấu trúc struct trong C vào các bài tập thực tiễn. Các bạn đọc qua & 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 soát bổ sung thêm các tùy chọn/ tính 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.
1
2
3
4
5
6
7
8
9
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <stdio.hvàgt;
#include <string.hvàgt;
#include <math.hvàgt;
int
UCLN
(
int
α
,
int
ɓ
)
{
α
=
abs
(
α
)
;
ɓ
=
abs
(
ɓ
)
;
while
(
α *
ɓ
!
=
)
{
if
(
α
>
ɓ
)
α
%
=
ɓ
;
else
ɓ
%
=
α
;
}
return
α
+
ɓ
;
}
int
BSCNN
(
int
α
,
int
ɓ
)
{
return
α *
ɓ
/
UCLN
(
α
,
ɓ
)
;
}
typedef
struct
PhanSo
{
int
tuso
,
mauso
;
}
PS
;
PS
rutGon
(
PS
α
)
{
PS
ͼ
;
ͼ
.
tuso
=
α
.
tuso
/
UCLN
(
α
.
tuso
,
α
.
mauso
)
;
ͼ
.
mauso
=
α
.
mauso
/
UCLN
(
α
.
tuso
,
α
.
mauso
)
;
return
ͼ
;
}
PS
cong
(
PS
α
,
PS
ɓ
)
{
PS
ͼ
;
ͼ
.
tuso
=
α
.
tuso *
ɓ
.
mauso
+
α
.
mauso *
ɓ
.
tuso
;
ͼ
.
mauso
=
α
.
mauso *
ɓ
.
mauso
;
ͼ
=
rutGon
(
ͼ
)
;
return
ͼ
;
}
PS
tru
(
PS
α
,
PS
ɓ
)
{
PS
ͼ
;
ͼ
.
tuso
=
α
.
tuso *
ɓ
.
mauso
–
α
.
mauso *
ɓ
.
tuso
;
ͼ
.
mauso
=
α
.
mauso *
ɓ
.
mauso
;
ͼ
=
rutGon
(
ͼ
)
;
return
ͼ
;
}
PS
nhan
(
PS
α
,
PS
ɓ
)
{
PS
ͼ
;
ͼ
.
tuso
=
α
.
tuso *
ɓ
.
tuso
;
ͼ
.
mauso
=
α
.
mauso *
ɓ
.
mauso
;
ͼ
=
rutGon
(
ͼ
)
;
return
ͼ
;
}
PS
chia
(
PS
α
,
PS
ɓ
)
{
PS
ͼ
;
ͼ
.
tuso
=
α
.
tuso *
ɓ
.
mauso
;
ͼ
.
mauso
=
α
.
mauso *
ɓ
.
tuso
;
ͼ
=
rutGon
(
ͼ
)
;
return
ͼ
;
}
void
(
PS
α
)
{
printf
(
“%d/%d”
,
α
.
tuso
,
α
.
mauso
)
;
}
int
main
(
)
{
PS
α
,
ɓ
,
ͼ
;
printf
(
“nNhap phan so a : “
)
;
scanf
(
“%d%d”
,
&α.tuso, &α.mauso);
printf
(
“nNhap phan so b : “
)
;
scanf
(
“%d%d”
,
&ɓ.tuso, &ɓ.mauso);
printf
(
“nToi gian a ta duoc : “
)
;
α
=
rutGon
(
α
)
;
(
α
)
;
printf
(
“nToi gian b ta duoc : “
)
;
ɓ
=
rutGon
(
ɓ
)
;
(
ɓ
)
;
printf
(
“nTong cua hai phan so = “
)
;
ͼ
=
cong
(
α
,
ɓ
)
;
(
ͼ
)
;
printf
(
“nHieu cua hai phan so = “
)
;
ͼ
=
tru
(
α
,
ɓ
)
;
(
ͼ
)
;
printf
(
“nTich cua hai phan so = “
)
;
ͼ
=
nhan
(
α
,
ɓ
)
;
(
ͼ
)
;
printf
(
“nThuong cua hai phan so = “
)
;
ͼ
=
chia
(
α
,
ɓ
)
;
(
ͼ
)
;
}
Kết quả chạy:
1
2
3
4
5
6
7
8
9
10
11
12
13
Nhap phan so α : 3
4
Nhap phan so ɓ : 2
3
Toi gian α ta duoc : 3/4
Toi gian ɓ 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 & con trỏ
Cũng giống như khai báo con trỏ với các kiểu dữ liệu có sẵn trong C. Tất 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 tất cả chúng ta khai báo biến con trỏ kiểu struct trong C:
1
2
3
4
5
6
7
8
9
10
11
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 member sử dụng biến con trỏ của struct trong C, bạn dùng ->
, chẳng hạn:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.hvàgt;
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
;
}
Trong chẳng hạn này, địa chỉ của biến person1
được cất giữ bởi con trỏ personPtr
. & bạn có thể thao tác với biến con trỏ giống như tất cả 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ớ lưu trữ động
Trước khi độc giả phần này, mình hi vọng các bạn đã sở hữu học thức về cấp phát động trong C.
Thỉnh thoảng, số lượng biến struct trong C mà tất cả chúng ta phải có thể lớn. Khi đó có thể bạn sẽ cần tới cấp phát động trong công cuộc chương trình thực thi. Dưới đây là phương pháp để cấp phát bộ nhớ lưu trữ động với kiểu cấu trúc:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.hvàgt;
#include <stdlib.hvàgt;
struct
person
{
int
age
;
float
weight
;
char
name
[
30
]
;
}
;
int
main
(
)
{
struct
person *
ptr
;
int
ι
,
ռ
;
printf
(
“Enter the number of persons: “
)
;
scanf
(
“%d”
,
&ռ);
// allocating memory for ռ numbers of struct person
ptr
=
(
struct
person*
)
malloc
(
ռ *
sizeof
(
struct
person
)
)
;
for
(
ι
=
;
ι
<
ռ
;
++
ι
)
{
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
+
ι
)
–
>
name
,
&(ptr+ι)->age);
}
printf
(
“Displaying Information:n”
)
;
for
(
ι
=
;
ι
<
ռ
;
++
ι
)
printf
(
“Name: %stAge: %dn”
,
(
ptr
+
ι
)
–
>
name
,
(
ptr
+
ι
)
–
>
age
)
;
return
;
}
Kết quả khi chạy chương trình:
1
2
3
4
5
6
7
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 chẳng hạn trên, sau khoảng thời gian người dùng nhập số lượng ռ
thì ta mới tiến hành cấp phát đúng ռ
ô nhớ sử dụng dòng lệnh này:
1
2
ptr
=
(
struct
person*
)
malloc
(
ռ *
sizeof
(
struct
person
)
)
;
& sau đó, ta dùng con trỏ ptr
để truy cập vào các member 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 & xếp đặt mục lục sinh viên theo điểm sử dụng cấp phát động cho con trỏ trong C++ (new & delete):
1
2
3
4
5
6
7
8
9
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#includevàlt;iostreamvàgt;
#includevàlt;stdio.hvàgt;
#includevàlt;math.hvàgt;
#includevàlt;string.hvàgt;
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 *
ρ
,
int
ռ
)
{
for
(
int
ι
=
;
ι
<
ռ
;
ι
++
)
{
cout
<
<
“Nhap thong tin cua sv thu “
<
<
ι
+
1
<
<
” :”
<
<
endl
;
Nhapsv
(
ρ
+
ι
)
;
}
}
void
Hienthids
(
SV *
ρ
,
int
ռ
)
{
for
(
int
ι
=
;
ι
<
ռ
;
ι
++
)
{
Hienthisv
(
ρ
+
ι
)
;
cout
<
<
“n”
;
}
}
void
Sapxep
(
SV *
ρ
,
int
ռ
)
{
for
(
int
ι
=
;
ι
<
ռ
;
ι
++
)
{
for
(
int
j
=
ι
+
1
;
j
<
ռ
;
j
++
)
if
(
(
ρ
+
ι
)
–
>
diem
>
(
ρ
+
j
)
–
>
diem
)
{
SV
tmp
=
*
(
ρ
+
j
)
;
*
(
ρ
+
j
)
=
*
(
ρ
+
ι
)
;
*
(
ρ
+
ι
)
=
tmp
;
}
}
}
int
main
(
)
{
SV *
ρ
;
int
ռ
;
do
{
cout
<
<
“Nhap vao so sv: “
;
cin
>
>
ռ
;
}
while
(
ռ
<
|
|
ռ
>
10
)
;
ρ
=
new
SV
[
n
]
;
cout
<
<
“Nhap vao thong tin “
<
<
ռ
<
<
” sv: “
<
<
endl
;
Nhapds
(
ρ
,
ռ
)
;
cout
<
<
“Hien thi thong tin vua nhap: “
<
<
endl
;
Hienthids
(
ρ
,
ռ
)
;
cout
<
<
“nDanh sach sau khi sap xep la: “
<
<
endl
;
Sapxep
(
ρ
,
ռ
)
;
Hienthids
(
ρ
,
ռ
)
;
delete
ρ
;
return
;
}
Kết quả chạy:
1
2
3
4
5
6
7
8
9
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
43
44
45
46
47
48
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 khoảng thời gian sap xep la:
3 3
3-3-3 3 3
5 5
5-5-5 5 5
9 9
9-9-9 9 9
không chỉ thế, mình cũng có một bài chỉ dẫn cụ thể & chuyên sâu hơn: Bài tập làm chủ sinh viên sử dụng struct trong C. Các bạn tiếp tục đọc qua nội dung này nhé!
Ebook đọc qua