Mình có bài toán nhỏ như sau đố các bạn cho vui:
typedef struct
{
char c;
int i;
} mstruct;
Đố các bạn sizeof(mstruct) là bao nhiêu byte. Chú ý: các expert không được trả lời nhé!
Mình dám cá là size của mstruct không phải là 5 bytes. Vì sao như vậy? Theo đúng lý thuyết ta học thì size của struct phải là tổng size của các thành phần bên trong nó mà. Vậy tại sao không phải là 5 bytes, có gì huyền bí ở đây?
Để hiểu rõ về việc này, chúng ta hãy cùng tìm hiểu sơ qua Data Structure Alignment & Padding.
Data structure alignment là gì? Nó hoạt động như thế nào? Lâu lâu chúng ta vẫn hay nghe software này không tương thích với hệ điều hành 64bit hay software này chỉ chạy trên HĐH 32bit gì đó, vậy liên quan gì tới data structure alignment không?
Bên cạnh đó chúng ta cần một số kiến thức nền về hệ thống để có thể hiểu sâu hơn. Mình sẽ cung cấp cho các bạn trong bài viết này (nhưng chỉ là tổng quan thôi, vì nó khá là chuyên sâu, nếu có thời gian mình sẽ viết thêm những bài về nó) và mình sẽ cung cấp keyword để các bạn có thể tra google thêm.
- Data Structure Alignment là gì?
Theo Wikipedia: Data Structure is the way data is arranged and accessed in computer memory. Có nghĩa là khi data load lên memory sẽ được CPU sắp xếp lại để tiện cho việc truy xuất tối ưu nhất có thể.
Chúng ta đều biết rằng các CPU hiện đại của chúng ta luôn thao tác trên memory theo từng khối ở địa chỉ là một số chẵn, không thể thao tác trên địa chỉ là số lẻ được. Như vậy với mstruct của chúng ta, ví dụ biến “char c” nằm trên memory có địa chỉ chẵn, nếu biến “int i” nằm kế tiếp thì sẽ có địa chỉ lẻ rất phức tạp để CPU thao tác với biến “int i” này. Vì vậy chúng ta có thêm 2 khái niệm sau:
- Data alignment: sắp xếp data sao cho địa chỉ của các biến luôn là số chẵn và phù hợp với hệ thống
- Data padding: để làm được việc alignment như ở trên chúng ta cần phải “padding” thêm một số byte vào sau biến “char c” để khi đó biến “int i” có thể ở địa chỉ chẵn
- Sau đây mình sẽ giới thiệu thêm về hệ thống.
- Trên các hệ thống khác nhau, chúng ta sẽ có sự khác nhau giữa sizecủa các data type như sau:
(chú ý “bool” không có trong C. Từ C99 trở đi ta có kiểu tương đương là _Bool)
- Lúc nãy ở trên chúng ta có biết được rằng CPU chỉ thao tác trên memory theo từng khối ở địa chỉ chẵn. Hay nói chính xác hơn khối ở đây là 1 WORD size. Tùy thuộc vào kiến trúc của CPU, nhưng thông dụng nhất có các size như sau:
- Trên system 16bit: 1 WORD = 16bit = 2 bytes
- Trên system 32bit: 1 WORD = 32bit = 4 bytes
- Trên system 64bit: 1 WORD = 64bit = 8 bytes
- N-byte alignment:
chúng ta có các loại n-byte alignment: 2-byte align, 4-byte align, 8-byte align.
Như vậy sizeof(mstruct) sẽ là bao nhiêu:
- Với system 16bit
sizeof(mstruct): 4 bytes
- Với system 32bit
sizeof(mstruct): 8 bytes
Thêm một ví dụ nữa để hiểu hơn:
typedef struct
{
char c; // 1 byte
double d; // 8 bytes
} mstruct2;
- Với system 32bit & 4-byte alignment
sizeof(mstruct2): 12 bytes
- Với system 32bit & 8-byte alignment
sizeof(mstruct2): 16 bytes
Như hình trên ta sẽ thấy rõ sự khác biệt giữa các n-byte alignment. N-byte alignment phụ thuộc vào compiler, chúng ta có thể điều chỉnh được.
Thêm 1 ví dụ nữa:
typedef struct
{
char c;
int i;
double d;
} mstruct3;
- Với system 32bit hay 64bit chúng ta luôn có sizeof(mstruct3) là 16 bytes. Nhưng việc sắp xếp trên memory sẽ khác.
- Với system 32bit
sizeof(mstruct3): 16 bytes
- Với system 64bit
sizeof(mstruct3): 16 bytes
Thêm ví dụ nữa:
typedef struct
{
char c;
int i;
short s;
double d;
} mstruct4;
- System 32 bit & 4 byte alignment
sizeof(mstruct4): 20 bytes
- System 32 bit & 8 byte alignment
sizeof(mstruct4): 24 bytes
- System 64 bit
sizeof(mstruct4): 24 bytes
Ví dụ nữa:
typedef struct
{
char c;
short s;
} mstruct5;
Các bạn thử tính toán xem size của mstruct5 là bao nhiêu trên các hệ thống 32bit/64bit với các n-byte alignment khác nhau.
Kết luận: Hy vọng các bạn đã hiểu được đôi chút về data structure alignment. Có thắc mắc gì hay có đoạn code nào hay các bạn post lên chia sẻ với mọi người nhé.