Đặt vấn đề
Giả sử STDIO tổ chức lưu trữ thông tin của các tác giả bằng ngôn ngữ C và có hơn 50 account được cấp quyền author (Gọi tắt là M).
Mỗi author cần lưu trữ một số thông tin, cụ thể như sau:
- Name.
- Job.
- Phone number.
- Articles (Danh sách bài viết của tác giả đó).
- About.
- ...
Theo thông thường, tạo ra các mảng tương ứng: char* name[M]
, char* job[M]
, long phone_number[M]
, char* articles[M]
, char* about[M]
, ...
Giả sử STDIO manager quản lý các author, muốn cập nhật thông tin cho một author, có prototype của hàm như sau:
void UpdateAuthorInfo(
char* &name, char* &job, long &phone_number, char* &articles, char* &about);
Vấn đề trở nên phức tạp khi STDIO quyết định bổ sung một vài thông tin cho author. Khi đó, không phải chỉ khai báo thêm các biến, mà các hàm liên quan cũng cần thay đổi theo, dễ xảy ra tình trạng sơ sót khi hệ thống ngày càng lớn.
Để giải quyết vấn đề đó, STDIO quyết định nâng cấp hệ thống bằng cách sử dụng struct
, gom nhóm các thông tin kể trên và biểu diễn thành một đối tượng duy nhất - SAuthor
(Cách thức gom nhóm sẽ được trình bày ở phần dưới). Lúc này các mảng lưu trữ thông tin ở trên sẽ được thay bằng một mảng duy nhất: SAuthor* authors[M]
. Khi đó, hàm UpdateAuthorInfo
được tối giản lại như sau:
void UpdateAuthorInfo(SAuthor* &author);
Mỗi đối tượng SAuthor
đại diện cho một author, lưu trữ tất cả thông tin trong cùng một cấu trúc dữ liệu. Khi cần cập nhật thêm tính năng, STDIO manager sẽ khai báo thêm thành phần bên trong cấu trúc SAuthor
và xử lý thêm tại các hàm liên quan.
Ở ví dụ nói trên, làm rõ luận điểm: Sử dụng struct để trừu tượng hoá các thông tin có liên quan với nhau thành một đối tượng duy nhất. Mặt khác, struct sau khi được định nghĩa có thể sử dụng như bất cứ kiểu dữ liệu thông thường nào, do đó Lập trình viên dễ dàng tiếp cận với struct như với các kiểu dữ liệu cơ bản.
Khai báo và sử dụng
Để khai báo một struct
, sử dụng cú pháp như sau:
struct SExample { <data_type> m_variable; // ... };
Lưu ý dấu chấm phẩy ; sau khi khai báo struct
là bắt buộc. Các thành phần của struct
được khai báo giống với một biến bình thường, cách nhau bởi dấu chấm phẩy. Các thành phần này có thể được truy cập thông qua các toán tử .
hay ->
(tương ứng với khai báo bình thường và khai báo kiểu con trỏ).
Như vậy, với ví dụ ở phần Đặt vấn đề, STDIO manager có thể lập trình cấu trúc SAuthor
như sau:
struct SAuthor { char* m_name; char* m_job; long m_phoneNumber; char* m_articles; char* m_about; // ... };
Một
struct
có thể chứastruct
khác như một thành phần của nó. Như vậy, có thể sử dụng liên tục các toán tử.
hoặc->
để truy xuất đến thành phần của các thành phần (struct
) này.
Tiền tố S
trong SExample
và m_
trong m_variable
ở ví dụ trên có thể được loại bỏ. Tuy nhiên, với STDIO các tiền tố này nên được sử dụng để phân biệt với các cấu trúc và kiểu dữ liệu khác. Có thể tham khảo thêm bài viết STDIO Coding Convention để xây dựng quy tắc riêng cho mình.
Sau khi khai báo, struct
có thể được đặt lại tên khác bằng từ khoá typedef
. Có thể xem thêm bài viết typedef Và enum.
Ứng dụng struct
Ngày nay, cùng với sự phát triển của ngôn ngữ C++ thì struct
vẫn được lập trình viên sử dụng trong nhiều trường hợp.
Biểu diễn Hình chữ nhật. Để biểu diễn một hình chữ nhật, cần chiều dài và chiều rộng, cùng với một điểm xác định "vị trí" của hình (thường là toạ độ của điểm top-left). Nếu hình chữ nhật này chỉ được sử dụng 1 lần, ví dụ trong trường hợp khởi tạo một cửa sổ Windows, có thể bỏ qua khái niệm struct
. Tuy nhiên nếu việc sử dụng được lặp đi lặp lại thì một cách tự nhiên, có khai báo struct
như sau:
struct SRectangle { float x, y; float width, height; };
Ngoài ra, có thể khai báo kết hợp để tạo ra cấu trúc SRectangle
như sau:
struct SVector2 { float x, y; }; struct SRectangle { struct SVector2 m_topLeft; float width, height; };
Biểu diễn Sin
(project Sins):
struct Sin { struct Sprite* m_sin; struct SColor3B m_color; struct Sprite* m_darkForce; bool m_isSealed; struct Animate* m_effect; };