Search…

Struct Alignment trong C++

10/09/20205 min read
Tìm hiểu về Struct Alignment trong C/C++.

C/C++ cung cấp cho chúng ta một số kiểu dữ liệu cơ bản như char, int, float, double, long... C/C++ cũng cho phép chúng ta tạo ra những kiểu dữ liệu mới tổng hợp nhiều kiểu dữ liệu bằng cách dùng struct, class. Việc cấp phát và tổ chức bộ nhớ đối với dữ liệu cơ bản 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.

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 1 byte -127 to 127 or 0 to 255
unsigned char 1 byte 0 to 255
signed char 1 byte -127 to 127
int 4 bytes   -2147483648 to 2147483647
unsigned int 4 bytes  0 to 4294967295
signed int 4 bytes -2147483648 to 2147483647
short int 2 bytes -32768 to 32767
unsigned short int   2 bytes 0 to 65,535
signed short int   2 bytes -32768 to 32767
long int  4 bytes -2,147,483,647 to 2,147,483,647
signed long int 4 bytes -2,147,483,647 to 2,147,483,647
unsigned long int 4 bytes 0 to 4,294,967,295
float       4 bytes +/- 3.4e +/- 38 (~7 digits)
double    8 bytes +/- 1.7e +/- 308 (~15 digits)
long double 8 bytes +/- 1.7e +/- 308 (~15 digits)

Trước khi đi vào chủ đề của bài viết bạn cần nắm 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:

int id = 13520546;        // 4 bytes
double salary = 500.000;  // 8 bytes

Và tổ chức bộ nhớ sẽ là:

Vùng nhớ của id và salary có thể là không liên tụ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:

int id = 13520546;
double salary = 500.000;
printf("id address: %d\nsalary address: %d\n", &id, &salary);

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:

struct Point
{
	int x;
	int y;
};

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++, kích thước của nó là 8 bytes đúng bằng các kích thước các trường dữ liệu thành viên cộng lạ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ì:

struct Student
{
	char id;
	int age;
	double gpa;
};

Lấy kích thước của của struct Student:

int size = sizeof(Student);

Kích thước mong đợi là 13 bytes bằng kích thước của id (1 bytes) cộng với age (4 bytes) và gpa (8 bytes). Nhưng thực tế kích thước có thể là 16 bytes.

Đầ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à 8 bytes 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à 8 bytes, id với kích thức 1 bytes sẽ ở vị trí như hình dưới:

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 4 bytes vào:

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

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

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, gpa lần lượt là 0x04, 0x08, 0xC.

Khảo sát thêm một struct nữa có tên là Demo

struct Demo
{
    int a, b;
    double c, d;
};

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

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

struct Demo
{
    int a;
    double c, d;
    int b;
};

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

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#. 

struct Person
{
    
};

1 struct Person không có thành viên và lấy kích thước của nó như bên dưới:

int size = sizeof(Person);

Kích thước sẽ là 1 byte. Đố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 byte để có thể lưu trữ nó dưới bộ nhớ.

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

Person person;
printf("Address of person %d\n", &person);

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

Đị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, đố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 đó.

struct Student
{
    char id;
    int age;
    double gpa;
};

int main()
{
	Student student;
	printf("Address of student: %d\n", &student);
	printf("Address of student.id: %d\n", &student.id);
	printf("Address of student.age: %d\n", &student.age);
	printf("Address of student.gpa: %d\n", &student.gpa);
    return 0;
}

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 đó:

IO Stream

IO Stream Co., Ltd

30 Trinh Dinh Thao, Hoa Thanh ward, Tan Phu district, Ho Chi Minh city, Vietnam
+84 28 22 00 11 12
developer@iostream.co

383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2024