C语言结构体之内存对齐
C语言结构体之内存对齐
1、什么是内存对齐
首先看一个例子,下面有一个结构体:
struct structTest1 { char c1; short s; char c2; int i; };
假设这个结构体成员在内存中是紧凑排列的,那么c1的存储地址就是0,s的存储地址是1-2,c2的存储地址是3,i的存储地址是4-7,c1的地址是0000000000000000,s的地址是0000000000000001,c2的地址是0000000000000003,i的地址是0000000000000004,整个结构体所占内存是8。但是写一个程序输出一下,则会得到不同的结果。
#include <stdio.h> struct structTest1 { char c1; short s; char c2; int i; }; int main() { struct structTest1 s1; printf("sizeof(s1): %d\n", sizeof(s1)); printf("c1:%p\ns :%p\nc2:%p\ni :%p\n",\ (unsigned int)(void*)&s1.c1 - (unsigned int)(void*)&s1,\ (unsigned int)(void*)&s1.s - (unsigned int)(void*)&s1,\ (unsigned int)(void*)&s1.c2 - (unsigned int)(void*)&s1,\ (unsigned int)(void*)&s1.i - (unsigned int)(void*)&s1); return 0; }
程序的输出结果与我们预测的结果不一致,这就是因为内存对齐导致的。
内存对齐就是,按照成员的声明顺序,依次安排内存,其偏移量为最大成员大小的整数倍,最后结构体的大小为最大成员的整数倍。
2、为什么会有内存对齐
因为访问未对齐的内存,处理器需要做两次内存访问,然而对齐的内存访问仅需要一次访问。缺省情况下,编译器默认将结构、栈中的成员数据进行内存对齐。编译器将未对齐的成员往后移,将每一个成员都对齐到自然边界上,从而也导致了整个结构体的尺寸变大。尽管会牺牲一点空间(成员间有部分内存空闲),但提高了性能。
3、如何避免内存对齐的影响
一个小技巧就是将上面的结构体写成下面的形式:
struct structTest1 { char c1; short s; char c2; int i; };
这样一来,每个成员都对齐在其自然边界上,从而避免了编译器自动对齐。
#include <stdio.h> struct structTest1 { char c1; char c2; short s; int i; }; int main() { struct structTest1 s1; printf("sizeof(s1): %d\n", sizeof(s1)); printf("c1:%p\nc2:%p\ns :%p\ni :%p\n",\ (unsigned int)(void*)&s1.c1 - (unsigned int)(void*)&s1,\ (unsigned int)(void*)&s1.c2 - (unsigned int)(void*)&s1,\ (unsigned int)(void*)&s1.s - (unsigned int)(void*)&s1,\ (unsigned int)(void*)&s1.i - (unsigned int)(void*)&s1); return 0; }
除此之外我们还可以利用#pragma pack()来改变编译器的默认对齐方式
使用指令#pragma pack (n),编译器将按照 n 个字节对齐。
使用指令#pragma pack (),编译器将取消自定义字节对齐方式。
在#pragma pack (n)和#pragma pack ()之间的代码按 n 个字节对齐。
当n=1时:
#include <stdio.h> #pragma pack(1) struct TestStruct1 { char a; long b; }; struct TestStruct2 { char c; struct TestStruct1 d; long long e; }; #pragma pack() int main() { printf("sizef(TestStruct2): %d\n", sizeof(struct TestStruct2)); return 0; }
当n=2时
#include <stdio.h> #pragma pack(2) struct TestStruct1 { char a; long b; }; struct TestStruct2 { char c; struct TestStruct1 d; long long e; }; #pragma pack() int main() { printf("sizef(TestStruct2): %d\n", sizeof(struct TestStruct2)); return 0; }
当n=4时
#include <stdio.h> #pragma pack(4) struct TestStruct1 { char a; long b; }; struct TestStruct2 { char c; struct TestStruct1 d; long long e; }; #pragma pack() int main() { printf("sizef(TestStruct2): %d\n", sizeof(struct TestStruct2)); return 0; }
当n=8时
#include <stdio.h> #pragma pack(8) struct TestStruct1 { char a; long b; }; struct TestStruct2 { char c; struct TestStruct1 d; long long e; }; #pragma pack() int main() { printf("sizef(TestStruct2): %d\n", sizeof(struct TestStruct2)); return 0; }
知行合一,
翻万卷书,游千里路,会百家才