C/C++

Struct Alignment Trong C++

Giới thiệu

C++ có cung cấp cho chúng ta một số kiểu dữ liệu nguyên thủy được xây dựng sẵn (built-in) như char, int, float, double, long… C++ cũng cho phép chúng ta tạo ra những kiểu dữ liệu mới do người dùng định nghĩa như struct, class. Việc cấp phát và tổ chức bộ nhớ đối với dữ liệu kiểu nguyên thủy là khá đơn giản, còn đối với struct, class thì cấp phát, tổ chức bộ nhớ như thế nào. Trong bài viết này tôi sẽ phân tích, làm rõ vấn đề này một cách cụ thể.

Tiền đề bài viết

Bài viết mong muốn chia sẽ kiến thức hữu ích cho các độc giả.
Những ví dụ trong bài viết tôi sử dụng Visual Studio 2013 trên hệ điều hành Windows 8.1 64bit.

Các kiểu dữ liệu nguyên thuỷ trong C++

C++ có cung cấp cho chúng ta các kiểu dữ liệu được xây dựng sẵn. Thông tin chi tiết về các kiểu dữ liệu này được thể hiện ở bảng dưới:

KIỂU DỮ LIỆU KÍCH THƯỚC MIỀN GIÁ TRỊ
char 1byte -127 to 127 or 0 to 255
unsigned char 1byte 0 to 255
signed char 1byte -127 to 127
int 4bytes -2147483648 to 2147483647
unsigned int 4bytes  0 to 4294967295
signed int 4bytes -2147483648 to 2147483647
short int 2bytes -32768 to 32767
unsigned short int 2bytes 0 to 65,535
signed short int 2bytes -32768 to 32767
long int 4bytes -2,147,483,647 to 2,147,483,647
signed long int 4bytes -2,147,483,647 to 2,147,483,647
unsigned long int 4bytes 0 to 4,294,967,295
float 4bytes +/- 3.4e +/- 38 (~7 digits)
double 8bytes +/- 1.7e +/- 308 (~15 digits)
long double 8bytes +/- 1.7e +/- 308 (~15 digits)

Trước khi đi vào chủ đề của bài viết tôi muốn các bạn hiểu rõ những biến này được tổ chức như thế nào trên bộ nhớ. Giả sử tôi có những câu lệnh dưới đây:

Và tổ chức bộ nhớ của chúng trong chương trình.

struct_alignment_ss_0

Vùng nhớ của id và salary có thể là không liên tục và nằm rời rạc trên bộ nhớ.

Kết quả khi chạy chương trình dưới đây và in ra địa chỉ của hai biến id và salary:

struct_alignment_ss_1

Tổ chức bộ nhớ trong struct, class

Tôi tạo một struct Point thể hiện tọa độ của điểm trong không gian hai chiều:

Tôi lấy kích thước của struct bằng cách sử dụng toán tử sizeof được cung cấp trong C++, và tôi thấy được kích thước của nó là 8bytes đúng bằng các kích thước các trường dữ liệu thành viên công lại. Và dễ hình dùng hơn struct Point này được tổ chức trên bộ nhớ như sau:

struct_alignment_ss_2

Tiếp thep tôi tạo tiếp một struct Student thể hiện thông tin của một sinh viên gồm id, age, gpa là điểm trung bình học kì:

Tôi tiếp tục lấy sizeof của struct Student:

Các bạn nghĩ rằng biến size sẽ có giá trị là 13bytes bằng kích thước của id (1bytes) cộng với age (4bytes) và gpa (8bytes). Nhưng thực tế sau khi debug tôi thấy rằng biến size có giá trị là 16bytes chứ không phải là 13bytes. Vậy struct được cấp phát và tổ chức như thế nào ở trên bộ nhớ?.

Đầu tiên trình biên dịch sẽ lấy kích thước của trường dữ liệu thành viên có kích thước lớn nhất (theo đơn vị byte) mà kích thước đó là lũy thừa của 2.

Trong trường hợp này là 8bytes theo kích thước của gpa. Sau đó cấp phát 1 block gồm đúng bằng kích thước này là 8bytes, và “đẩy” id có kích thức 1 bytes vào:

struct_alignment_ss_3

Ta vẫn còn 7 chổ trống, ở đây tùy theo trình biên dịch mà nó sẽ padding bao nhiêu bytes rồi “đẩy” age kiểu int có kích thước 4bytes vào. Ở đây tôi sử dụng trình biên dịch của Visual Stdio 2013 nên sẽ padding 3bytes sau đó “đẩy” age vào:

struct_alignment_ss_4

Đã hết chổ trống, tiếp tục cấp thêm block 8 byte nữa và “đẩy” gpa kiểu double có kích thước 8byte vào:

struct-alignment_ss_5

Vậy kích thước của struct này là 16bytes.

Vùng nhớ cấp phát cho struct luôn luôn là liên tục trong bộ nhớ như ở trên ta thấy là địa chỉ của id, age, gpalần lượt là 0x04, 0x08, 0xC.

Tôi khảo sát thêm một struct nữa có tên là Demo

Kích thước struct này là: 24bytes;

Tôi sắp xếp lại các trường dữ liệu như sau:

Kiểm tra thấy kích thức của struct này là 32bytes

Như vậy struct có cùng các trường dữ liệu như nhau, nhưng nếu thay đổi vị trí của chúng trong struct thì có thể thay đổi kích thước của struct đó. Dựa vào cách sắp xếp bộ nhớ trong struct mà tôi đã trình bày ở trên giúp các bạn có thể tránh được việc mất mát vùng nhớ.

Lưu ý: Cách tổ chức bộ nhớ trong class cũng tượng tự như struct.

Struct, class không có dữ liệu thành viên

Chúng ta đã khảo sát struct có các trường dữ liệu, nếu struct đó không có dữ liệu thành viên thì sao, việc lưu trữ như thế nào?. Một ví dụ điển hình là class xử lý toán học Math trong C#.

Tôi có 1 struct Person không có thành viên nào cả. Và tôi lấy sizeof của nó:

Debug thấy size có giá trị 1bytes. Thì đối với những struct, hay class như thế này thì trình biên dịch sẽ cấp phát 1 bytes để có thể lưu trữ nó dưới bộ nhớ.

Tiếp theo, tôi tạo 1 biến có tên là person và lấy địa chỉ của nó trong bộ nhớ:

Kết quả in lên màn hình

struct_alignment_ss_6

Địa chỉ của struct, class

Cũng giống như mảng, địa chỉ của mảng chính là địa chỉ của phần tử đầu tiên, thì đối với class, struct cũng vậy. Địa chỉ của nó chính là địa chỉ của thành viên đầu tiên trong struct, class đó.

Và kết quả cho thấy rằng địa chỉ của student cũng chính là địa chỉ của  id trong struct đó:

struct-alignment_ss_7

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.