Bài 8 · Nâng cao · 24 phút· Cập nhật 11/06/2026
Struct & con trỏ struct
Biên soạn bởi Nguyễn Anh Tuấn
Struct và con trỏ struct trong C: toán tử ->, malloc một struct trên heap, và danh sách liên kết (linked list) - struct trỏ tới chính kiểu của nó.
struct gộp nhiều trường (field) thành một kiểu mới. Có một struct giá trị, ta truy cập trường bằng dấu chấm (.):
struct.c
#include <stdio.h>
struct Diem {
int x;
int y;
};
int main() {
struct Diem p;
p.x = 3;
p.y = 4;
printf("%d %d\n", p.x, p.y); // 3 4
return 0;
} Kết quả khi chạy
3 4
- ▸struct Tên { kiểu trường1; kiểu trường2; ... };
- ▸Truy cập trường của một struct giá trị bằng dấu chấm: p.x.
- ▸struct cho phép tạo “kiểu dữ liệu của riêng bạn” từ các kiểu cơ bản.
Khi có một con trỏ tới struct, dùng mũi tên (->) để truy cập trường. pp->x chỉ là viết tắt của (*pp).x:
arrow.c
#include <stdio.h>
struct Diem { int x; int y; };
int main() {
struct Diem p = {3, 4};
struct Diem *pp = &p; // con tro toi p
printf("%d\n", pp->x); // 3 = (*pp).x
pp->y = 9; // sua p qua con tro
printf("%d\n", p.y); // 9
return 0;
} Kết quả khi chạy
3 9
- ▸p.x - khi p là struct GIÁ TRỊ.
- ▸pp->x - khi pp là CON TRỎ struct.
- ▸pp->x ≡ (*pp).x (dereference rồi lấy trường).
Ghép với bài trước: ta có thể malloc một struct trên heap. Kết quả là một con trỏ struct, nên truy cập trường bằng ->:
malloc-struct.c
#include <stdio.h>
#include <stdlib.h>
struct Diem { int x; int y; };
int main() {
struct Diem *p = malloc(sizeof(struct Diem));
p->x = 5;
p->y = 7;
printf("%d %d\n", p->x, p->y); // 5 7
free(p);
return 0;
} Kết quả khi chạy
5 7
Điểm lợi hại nhất: một struct chứa con trỏ tới chính kiểu của nó (struct Node *next) - tạo thành chuỗi nút nối nhau. Bấm Bước tiếp để dựng rồi duyệt danh sách 10 → 20 → 30 → NULL:
linked-list.c
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int val;
struct Node *next; // tro toi nut ke (chinh kieu Node)
} Node;
int main() {
Node *a = malloc(sizeof(Node)); a->val = 10;
Node *b = malloc(sizeof(Node)); b->val = 20;
a->next = b;
b->next = NULL; // cuoi danh sach
for (Node *p = a; p != NULL; p = p->next)
printf("%d ", p->val); // 10 20
free(b); free(a); // tra lai heap
return 0;
} Kết quả khi chạy
10 20
Bốn lỗi struct/con-trỏ thường gặp:
- ▸Nhầm . với ->: dùng . trên con trỏ (p.x khi p là Node *) → lỗi biên dịch. Có con trỏ thì dùng ->.
- ▸struct Node next (không có *) → kích thước vô hạn; phải là struct Node *next.
- ▸Duyệt mà quên kiểm tra p != NULL → deref NULL ở cuối danh sách → sập.
- ▸Quên free từng nút (mỗi nút một malloc) → rò rỉ bộ nhớ.
Giải phóng danh sách cho đúng
Câu hỏi thường gặp
Gói nhiều trường (field) có liên quan dưới MỘT kiểu/tên - ví dụ struct Diem { int x; int y; } gộp hai toạ độ. Thay vì truyền x, y rời rạc, ta truyền một Diem. struct là cách C xây “kiểu dữ liệu của riêng bạn”.
Dùng dấu chấm (.) khi bạn có một struct GIÁ TRỊ: p.x. Dùng mũi tên (->) khi bạn có một CON TRỎ struct: pp->x. pp->x chỉ là viết tắt của (*pp).x - dereference rồi lấy trường. Có con trỏ thì gần như luôn dùng ->.
Nếu một Node CHỨA nguyên một Node bên trong thì kích thước sẽ vô hạn (Node chứa Node chứa Node…). Con trỏ chỉ rộng 8 byte và TRỎ tới nút kế - nên struct tự tham chiếu luôn dùng con trỏ.
Mảng: các phần tử liền khối, truy cập a[i] tức thì, nhưng kích thước cố định và chèn/xoá giữa chừng tốn kém. Danh sách liên kết: các nút rải rác nối bằng con trỏ next, thêm/xoá dễ, lớn nhỏ tuỳ ý - nhưng phải đi TUẦN TỰ (không có a[i]) và mỗi nút tốn thêm bộ nhớ cho con trỏ.
Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.
Trả lời vài câu để chắc rằng em đã nắm bài.
Khi pp là một con trỏ struct, pp->x là viết tắt của biểu thức nào?
- 1
Điều kiện dừng
Trong mô phỏng ở Bước 4, ở bước cuối con trỏ
pbằng gì khiến vòngwhiledừng?Hoàn thành khi:
p = NULL(nextcủa nút cuốiclàNULL) ⇒while (p != NULL)sai ⇒ dừng. Đã in 10 20 30. - 2
Truy cập trường
struct Diem p; p.x = 3;- truy cập trườngybằng cú pháp nào?Hoàn thành khi:
p.y- dấu chấm vìplà struct giá trị (không phải con trỏ). - 3
Qua con trỏ
Có
struct Diem *pp = &p;thìpp->xtương đương cách viết nào dùng*?Hoàn thành khi:
(*pp).x-pp->xchính là viết tắt của nó. - 4
Struct tự tham chiếu
Vì sao
Nodephải khai báostruct Node *nextmà không phảistruct Node next?Hoàn thành khi:
struct Node nextsẽ làm kích thước vô hạn (đệ quy). Con trỏ (8 byte) chỉ TRỎ tới nút kế. - 5
Tổng danh sách
Danh sách 10 → 20 → 30 →
NULL. Viết vòng duyệt tính TỔNG cácval. Kết quả?Hoàn thành khi:
int s = 0; for (Node *p = a; p; p = p->next) s += p->val;→s = 60. - 6
Giải phóng đúng
Khi
freecả danh sách, vì sao phải lưup->nextTRƯỚC khifree(p)?Hoàn thành khi: Sau
free(p),p->nextkhông còn đọc được (vùng đã giải phóng) → mất con trỏ tới phần còn lại của danh sách.