1、什么是内存对齐?
将程序中的每个数据单元
安排在适当的位置
上(这是编译器干的事)
编译器将程序中的每个数据单元
安排在合适的位置上,C语言的灵活性允许程序员干预内存对齐
,如果想要了解更加底层的秘密,就不应该继续对内存对齐保持透明了
以下所有程序的运行环境都是windows 7,Dev-C++ IDE,编译器版本是TDM-GCC 4.9.2 64-bit Debug
//内存对齐计算验证实例
#include <stdio.h>
#include <stddef.h>
typedef struct test{
char a;
int b;
double c;
char d;
}Test;
int main(int argc, char ** argv)
{
Test T;
int offset_a = offsetof(Test, a);
int offset_b = offsetof(Test, b);
int offset_c = offsetof(Test, c);
int offset_d = offsetof(Test, d);
printf("The length of char type is %u\n", sizeof(char));
printf("The length of int type is %u\n", sizeof(int));
printf("The length of double type is %u\n\n", sizeof(double));
printf("The length of the whole struct is %u\n\n", sizeof(T));
printf("The offset of first data member is %d\n", offset_a);
printf("The offset of second data member is %d\n", offset_b);
printf("The offset of third data member is %d\n", offset_c);
printf("The offset of fourth data member is %d\n\n", offset_d);
printf("The begin address of first data member of struct test is %p\n", &(T.a));
printf("The begin address of second data member of struct test is %p\n", &(T.b));
printf("The begin address of third data member of struct test is %p\n", &(T.c));
printf("The begin address of fourth data member of struct test is %p\n\n", &(T.d));
return 0;
}
程序输出如下图所示:
2、需要内存对齐的原因
- 不是所有的硬件平台都能访问任意地址上的任意数据(某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常)
- 可以在相当大的程度上提高程序性能(
以空间换时间
)
3、内存对齐的主要应用范围
对于内存对齐问题,主要存在于struct和union
等复合结构在内存中的分布情况中(或者说在这两种复合结构中内存对齐的影响比较明显)
4、内存对齐的规则
原则1
:对于struct和union来说,其第一个数据成员要放在offset==0
的地方。如果第一个数据成员为某个复合结构的子成员,则要根据子成员的类型存放在对应的整数倍的地址上原则2
:结构体成员按自身长度自然对齐
(所谓自然对齐,指的是该成员的起始位置的内存地址必须是它自身长度的整数倍)。如果结构体作为成员,则要找到这个结构体中的最大元素,然后从这个最大成员长度的整数倍地址开始存储原则3
:结构体的总大小为结构体的有效对齐值
的整数倍
结构体的有效对齐值
的确定:
- 当未明确指定时,以结构体中最长成员的长度为其有效对齐值
- 当用
#pragma pack(n)
指定时,以n和结构体中最长成员的长度中较小者为其有效对齐值 - 当用
__attribute__ ((__packed__))
指定长度时,强制按照此值为结构体的有效对齐值
5、内存对齐带来的问题
编译器为了满足内存对齐要求,会在各个数据成员之间留下额外的内存空间,这会造成很小的浪费。但是这种浪费是可以容忍的
6、补充说明
- 内存对齐对于32位和64位平台有些许不同(因为32位的cpu一次最多能处理32bits的信息,64位的cpu一次最多能处理64bits的信息)
- 使用sizeof运算符可以得到struct和union的确切大小(其中包含了因内存对齐所产生的内存碎片)
- 可以使用offsetof宏来得到一个数据成员在结构体中的位置
- 编译器会对内存的分布进行优化,所以理论计算值和实际输出值有时会有些许不同
- wnidows的默认对齐数为8,Linux的默认对齐数是4(这两个数对
offset地址的选取
有影响) - 结构体中数据成员的顺序可能会影响整个结构体所占内存的大小
- 无论
#pragma pack
和__attribute__
如何指定,结构内部数据成员的自对齐仍然按照其自身的长度来对齐
#include <stdio.h>
#include <stddef.h>
typedef struct test{
int a; //原则1
double b; //原则2
float c; //原则3
}Test;
int main(int argc, char ** argv)
{
Test T;
int offset_a = offsetof(Test, a);
int offset_b = offsetof(Test, b);
int offset_c = offsetof(Test, c);
printf("The length of int type is %u\n", sizeof(int));
printf("The length of double type is %u\n", sizeof(double));
printf("The length of float type is %u\n\n", sizeof(float));
printf("The length of the whole struct is %u\n\n", sizeof(T));
printf("The offset of first data member is %d\n", offset_a);
printf("The offset of second data member is %d\n", offset_b);
printf("The offset of third data member is %d\n\n", offset_c);
printf("The begin address of first data member of struct test is %p\n", &(T.a));
printf("The begin address of second data member of struct test is %p\n", &(T.b));
printf("The begin address of third data member of struct test is %p\n\n", &(T.c));
return 0;
}
程序输出如下图所示:
#include <stdio.h>
#include <stddef.h>
typedef struct test{
int a;
double b;
float c;
}Test;
typedef struct test1{
char a[2];
int b;
double c;
short d;
Test e;
}Test1;
int main(int argc, char ** argv)
{
Test1 T;
int offset_a = offsetof(Test1, a);
int offset_b = offsetof(Test1, b);
int offset_c = offsetof(Test1, c);
int offset_d = offsetof(Test1, d);
int offset_e = offsetof(Test1, e);
printf("The length of char type is %u\n", sizeof(char));
printf("The length of int type is %u\n", sizeof(int));
printf("The length of double type is %u\n", sizeof(double));
printf("The length of short type is %u\n", sizeof(short));
printf("The length of Test type is %u\n\n", sizeof(Test));
printf("The length of the whole struct is %u\n\n", sizeof(T));
printf("The offset of first data member is %d\n", offset_a);
printf("The offset of second data member is %d\n", offset_b);
printf("The offset of third data member is %d\n", offset_c);
printf("The offset of fourth data member is %d\n", offset_d);
printf("The offset of fifth data member is %d\n\n", offset_e);
printf("The begin address of first data member of struct test is %p\n", &(T.a));
printf("The begin address of second data member of struct test is %p\n", &(T.b));
printf("The begin address of third data member of struct test is %p\n", &(T.c));
printf("The begin address of third data member of struct test is %p\n", &(T.d));
printf("The begin address of third data member of struct test is %p\n\n", &(T.e));
return 0;
}
程序输出如下图所示:
//不同的编译器会对内存的分布进行优化,例如有的编译器可能会将该程序优化成下一个程序
//这是属于编译器的问题,这里不做详细的讨论
//尽量在保持代码清晰的情况下,自己手动将该程序优化为下一个程序那样
//如果是单纯的做题,不用考虑编译器的优化问题,一切按照理论进行计算
#include <stdio.h>
#include <stddef.h>
typedef struct test{
char a;
int b;
char c;
}Test;
int main(int argc, char ** argv)
{
Test T;
int offset_a = offsetof(Test, a);
int offset_b = offsetof(Test, b);
int offset_c = offsetof(Test, c);
printf("The length of char type is %u\n", sizeof(char));
printf("The length of int type is %u\n\n", sizeof(int));
printf("The length of the whole struct is %u\n\n", sizeof(T));
printf("The offset of first data member is %d\n", offset_a);
printf("The offset of second data member is %d\n", offset_b);
printf("The offset of third data member is %d\n\n", offset_c);
printf("The begin address of first data member of struct test is %p\n", &(T.a));
printf("The begin address of second data member of struct test is %p\n", &(T.b));
printf("The begin address of third data member of struct test is %p\n\n", &(T.c));
return 0;
}
程序输出如下图所示:
#include <stdio.h>
#include <stddef.h>
typedef struct test{
char a;
char b;
int c;
}Test;
int main(int argc, char ** argv)
{
Test T;
int offset_a = offsetof(Test, a);
int offset_b = offsetof(Test, b);
int offset_c = offsetof(Test, c);
printf("The length of char type is %u\n", sizeof(char));
printf("The length of int type is %u\n\n", sizeof(int));
printf("The length of the whole struct is %u\n\n", sizeof(T));
printf("The offset of first data member is %d\n", offset_a);
printf("The offset of second data member is %d\n", offset_b);
printf("The offset of third data member is %d\n\n", offset_c);
printf("The begin address of first data member of struct test is %p\n", &(T.a));
printf("The begin address of second data member of struct test is %p\n", &(T.b));
printf("The begin address of third data member of struct test is %p\n\n", &(T.c));
return 0;
}
程序输出如图所示:
#include <stdio.h>
#include <stddef.h>
#pragma pack(2)
typedef struct test{
char a;
int b;
char c;
}Test;
int main(int argc, char ** argv)
{
Test T;
int offset_a = offsetof(Test, a);
int offset_b = offsetof(Test, b);
int offset_c = offsetof(Test, c);
printf("The length of char type is %u\n", sizeof(char));
printf("The length of int type is %u\n\n", sizeof(int));
printf("The length of the whole struct is %u\n\n", sizeof(T));
printf("The offset of first data member is %d\n", offset_a);
printf("The offset of second data member is %d\n", offset_b);
printf("The offset of third data member is %d\n\n", offset_c);
printf("The begin address of first data member of struct test is %p\n", &(T.a));
printf("The begin address of second data member of struct test is %p\n", &(T.b));
printf("The begin address of third data member of struct test is %p\n\n", &(T.c));
return 0;
}
程序输出如下图所示:
版权声明:本文为weixin_39918693原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。