🧱 1. Nhúng trực tiếp struct A vào struct B c struct A { int x; };
struct B { struct A a; // nhúng trực tiếp }; ✅ Dùng khi: Struct A là thành phần bắt buộc của B, luôn tồn tại cùng B.
Không cần cấp phát động hay quản lý bộ nhớ riêng cho A.
Hiệu năng quan trọng: truy cập nhanh, không qua con trỏ.
📌 Ưu điểm: Dễ dùng, không cần malloc hay free.
Không lo rò rỉ bộ nhớ.
Dữ liệu nằm liền mạch trong bộ nhớ → cache-friendly.
❌ Nhược điểm: Không thể chia sẻ A giữa nhiều B.
Không linh hoạt nếu A có kích thước lớn hoặc thay đổi động.
🧷 2. Dùng con trỏ đến struct A trong struct B c struct A { int x; };
struct B { struct A* a; // con trỏ }; ✅ Dùng khi: A có thể được chia sẻ giữa nhiều B.
A có thể thay đổi kích thước hoặc cấu trúc động.
Cần trì hoãn khởi tạo A (lazy init).
Muốn đa hình (struct A có thể là nhiều kiểu khác nhau qua con trỏ).
📌 Ưu điểm: Linh hoạt trong quản lý bộ nhớ.
Có thể thay thế A mà không thay đổi B.
Hữu ích trong các cấu trúc phức tạp như cây, danh sách liên kết, v.v.
❌ Nhược điểm: Cần quản lý bộ nhớ thủ công (malloc, free).
Dễ gây rò rỉ bộ nhớ hoặc lỗi truy cập nếu không cẩn thận.
Truy cập chậm hơn một chút do phải qua con trỏ.
🎯 3. Khởi tạo trực tiếp hay cấp phát động Khởi tạo trực tiếp (struct A a = {...}) → dùng khi biết trước dữ liệu, không cần thay đổi kích thước.
Cấp phát động (struct A* a = malloc(sizeof(struct A))) → dùng khi cần linh hoạt, hoặc khi struct quá lớn.
💡 Tổng kết lựa chọn Tình huống Nên dùng kiểu gì? A luôn đi kèm với B Nhúng trực tiếp A có thể được chia sẻ giữa nhiều B Dùng con trỏ A có kích thước lớn hoặc thay đổi động Dùng con trỏ + malloc Cần hiệu năng cao Nhúng trực tiếp Cần đa hình hoặc trừu tượng Dùng con trỏ