Mở đầu
Để có thể hiểu về contaier_of
, trước hết cần phải hiểu offsetof()
trong thư viện stddef.h
Thế offsetof()
: dùng để làm gì ?
- Macro này sẽ trả về 1 giá trị kiểu
size_t
. - Giá trị này là khoảng cách các byte của member trong struct.
Hãy xem ví dụ sau đây để có thể hiểu rõ hơn về cách sử dụng offsetof()
#include <stdio.h>
#include <stdint.h>
#include <stddef.h> struct Register{ uint8_t setup[2]; uint8_t input; uint8_t output[3];
}; int main()
{ printf("setup offset: %d\n", offsetof(struct Register, setup)); printf("input offset: %d\n", offsetof(struct Register, input)); printf("output offset: %d\n", offsetof(struct Register, output)); printf("Size cua struct Register: %d\n", sizeof(struct Register));
}
Output
setup offset: 0
input offset: 2
output offset: 3
Size cua struct Register: 6
Như có thể thấy kết quả ở đây, thì
setup
nằm ở vị trí đầu tiên, nênoffset
so vớistruct Register
sẽ là 0.input
nằm ở vị trí thứ 2, vàoffset
so vớistruct Register
là 2. Câu hỏi: "Tại sao lại là 2 mà không phải là giá trị nào khác ?"- Bởi vì 2 byte đầu tiên do,
setup
đã chiếm trongstruct Register
nêninput
nằm ở vị trí byte thứ 3.
- Bởi vì 2 byte đầu tiên do,
- Tương tự,
output
cóoffset
= 3 , chiếm vị trí bắt đầu ở byte 4.
Tại vì sao, tôi lại tìm hiểu vấn đề này ?
Trong quá trình phát triển 1 os theo kiến trúc event-driven
, os này dành cho dự án IoT cá nhân của tôi. Có 1 vấn đề xảy ra, cùng không hẳng là vấn đề hihi.
Mô tả như sau:
Tôi có 1 list gọi là Ready list - chứa những task sẵn sàng chạy Ở đây tôi dùng Double linked list để lưu trữ những task ready này.
struct Dlist
{ DNode first; DNode last; size; } struct DNode
{ DNode next; DNode prev;
} struct Task
{ DNode Node_task; // đại diện task trong ready list int data; ....
}
Đây là một đoạn mã đơn giãn để tôi có thể lưu trữ các task này vào DList, nhưng 1 vấn đề là khi mà ta lưu trữ task dưới dạng node trong list thì list đó sẽ không nhận diện được task nào đang nắm giữ node đó, mặc dù node có ở trong list.
- Lúc này ta cần phải tạo 1 con trỏ để có thể trỏ tới task đang nắm giữa node này
Lúc này, cần phải chỉnh sửa struct DNode
một tý
struct DNode
{ struct Task* Qwner; DNode next; DNode prev;
}
Lúc này thì con trỏ Qwner
sẽ trỏ tới task dang nắm giữ node này. OK => Thế là vấn đề trên đã được giải quyết.
Nhưng chưa dùng lại ở đó, tôi đã tìm hiểu được 1 pp hay hơn, không cần phải thêm con trỏ vào struct như trên => Tiết kiệm được 1 phần memory.
Đó chỉnh là sử dụng container_of
Khái niệm: dùng địa chỉ của member trong struct để có thể tính ra được địa chỉ của struct đang nắm giữa member này Theo ví dụ trên: ta sẽ dựa vào dịa chỉ của DNode chứa trong struct Task, để có thể tìm ra dược địa chỉ của Task nắm giữa node này.