Nội dung bài viết
STDIO Struct là một khái niệm ra đời từ rất sớm trong lịch sử phát triển ngôn ngữ lập trình C. Việc sử dụng struct giúp hệ thống hoá các đối tượng, tránh tình trạng rời rạc và hỗ trợ lập trình viên tăng cường tư duy trong lập trình.

Giới thiệu

Struct là một khái niệm ra đời từ rất sớm trong lịch sử phát triển ngôn ngữ lập trình C. Việc sử dụng struct giúp hệ thống hoá các đối tượng, tránh tình trạng rời rạc và hỗ trợ lập trình viên tăng cường tư duy trong lập trình. Bài viết này ngoài nhằm hướng dẫn và hỗ trợ kĩ thuật về struct, tôi sẽ giúp các bạn tư duy trừu tượng qua một vài ví dụ cụ thể.

Tiền đề bài viết

Kiến thức về struct khá đơn giản và dễ học, tuy nhiên việc tư duy là vô cùng quan trọng và là tiền đề để các bạn tìm hiểu sâu hơn về lập trình sau này. Bài viết là món quà tinh thần cho một vài thành viên thuộc STDIO Training, và là kinh nghiệm của bản thân tôi sau một thời gian dài tìm hiểu sâu về nền tảng ngôn ngữ C. Cảm ơn anh La Kiến Vinh đã gợi ý và anh Ryan Lê, Đạt Trương đã cùng thảo luận với em để em có thể hoàn thành bài viết này.

Đối tượng hướng đến

Những lập trình viên đang bắt đầu tìm hiểu về ngôn ngữ C đều có thể theo dõi bài viết này. Không đi sâu về kĩ thuật, bài viết sẽ tập trung vào tính thực tế và các giải pháp lập trình.

Các đối tượng khác cũng có thể xem bài viết như một tài liệu tham khảo.

Đặt vấn đề

Giả sử STDIO tổ chức lưu trữ thông tin của các Author bằng ngôn ngữ C. Ở thời điểm hiện tại, 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, ta sẽ 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, ta 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, tôi muố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, ta 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ứa struct khác như một thành phần của nó. Như vậy, ta 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 SExamplem_ trong m_variable ở ví dụ trên có thể được loại bỏ. Tuy nhiên, với STDIO - và cũng là nguyên tắc làm việc của tôi, 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. Bạn đọc có thể tham khảo thêm bài viết STDIO Coding Convention - Level 1 của tác giả La Kiến Vinh để 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. Bạn đọc có thể xem thêm bài viết typedef Và enum của tác giả Lê Minh Trung.

Ứ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. Đối với STDIO cũng vậy, một project dù lớn nhỏ đều không thể thiếu sự có mặt của struct. Nhờ tính mở của nó (các thành phần đều có thể dễ dàng truy xuất), struct thường được sử dụng để xây dựng các cấu trúc dữ liệu đơn giản. Một số ví dụ sử dụng struct trong các project thực tế của STDIO:

Biểu diễn Hình chữ nhật. Để biểu diễn một hình chữ nhật, ta 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, ta 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, ta có khai báo struct như sau:

struct SRectangle
{
	float x, y;
	float width, height;
};

Ngoài ra, ta 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;
};

Trên đây là một số thành phần cơ bản tôi trích ra từ project làm ví dụ minh hoạ cho các bạn. Project thực tế có độ phức tạp cao hơn nên tôi sẽ dành một bài viết khác để thảo luận sâu hơn.

Tổng kết

Bài viết nhằm cung cấp kiến thức ở mức độ nền tảng, đồng thời mở ra cho các bạn cách tư duy mở trong công việc lập trình. Qua bài viết, hi vọng các bạn có cái nhìn khác về struct và sử dụng phù hợp với mục đích cá nhân trong công việc.

Việc lập trình không hề bị ràng buộc bởi bất kỳ công thức nào. Với tư duy sáng tạo không giới hạn, bạn có thể thoả sức mình để tạo ra các sản phẩm độc đáo mang tính cá nhân hoá.

THẢO LUẬN
ĐÓNG