您的位置:首页 > 编程语言 > C语言/C++

C语言精要总结-内存地址对齐与struct大小判断篇

2017-04-29 15:08 483 查看
在笔试时,经常会遇到结构体大小的问题,实际就是在考内存地址对齐。在实际开发中,如果一个结构体会在内存中高频地分配创建,那么掌握内存地址对齐规则,通过简单地自定义对齐方式,或者调整结构体成员的顺序,可以有效地减少内存使用。另外,一些不用边界对齐、可以在任何地址(包括奇数地址)引用任何数据类型的的机器,不在本文讨论范围之内。

什么是地址对齐

计算机读取或者写入存储器地址时,一般以字(因系统而异,32位系统为4个字节)大小(N)的块来执行操作。数据对齐就是将数据存储区的首地址对齐字大小(N)的某个整数倍地址。为了对齐数据,有时需要在物理上相邻的两个数据之间保留或者插入一些无意义的字节。内存对齐本事编译器考虑是事情,但在C、C++语言中,可以人为修改对齐方式。

为什么要地址对齐

计算机会保证存储器字的大小,至少要大于等于计算机支持的最大原始数据类型的大小。

这样,一个原始数据类型就一定可以存放在一个存储器字中,如果保证了数据是地址对齐的,那么访问一个原始数据就可以保证只访问一个存储器字,这有利于提高效率。如下图

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
/************************************************************************/
/* 这里约定:占用即表示本身大小及其后的空余空间
/************************************************************************/
struct MyStruct1    // 起始地址能被8整除
{
char a;            // 8
double b;        // 8
float c;        // 4
int d;            // 4
} m1;                // 24

struct MyStruct2    // 起始地址能被8整除
{
int a;            // 4
float b;        // 4
char c;            // 8 // 后面 double的起始地址要能被8 整除,所以c补齐8个字节
double d;        // 8
} m2;                // 24

struct MyStruct3    // 起始地址能被8整除
{
short a;        // 2
char b;            // 6 // 同理,后面的元素的起始地址要能被 8 整除,所以b只要占用6
double c;        // 8
int d;            // 8 // 需要在其后填充一些字节,以保证在分配数组之后,每个数组元素要满足起始地址约束
} m3;                // 24

struct MyStruct4
{
char a;            // 2 // 能被4整除的地址 +2之后能被2整除,所以a只要补1个字节
short b;        // 2
int c;            // 4
} m4;                // 8

struct MyStruct5    // 起始地址能被8整除
{
double a;        // 8
float b;        // 4
int c;            // 4
short d;        // 2
char e;            // 6 因为后面紧紧挨着的MyStruct5 变量(在分配数组的时候)起始地址也要能被8整除,所以这个结构体总的大小必须是8的整数倍
} m5;                // 24

struct MyStruct6    // 除4对齐
{
short a;        // 2
char b;            // 2
long c;            // 4
short d;        // 4  // 保证数组后面的元素也符合规则 (结构体首地址可以除4)
} m6;                // 12

struct MyStruct7    // 4 对齐
{
int a;            // 4
char b;            // 2
short c;        // 2
char d[6];        // 8
} m7;                    // 16

int main(){
printf("m1 size : %d\n",sizeof m1);
printf("m2 size : %d\n",sizeof m2);
printf("m3 size : %d\n",sizeof m3);
printf("m4 size : %d\n",sizeof m4);
printf("m5 size : %d\n",sizeof m5);
printf("m6 size : %d\n",sizeof m6);
printf("m7 size : %d\n",sizeof m7);

// offsetof 函数用来计算成员离结构体首地址偏移的字节数
printf("MyStruct1 b offset : %d\n",offsetof(struct MyStruct1,b));    // b偏移8个字节,所以成员a占用8个字节
printf("MyStruct2 d offset : %d\n",offsetof(struct MyStruct2,d));    // d偏移了16个字节
printf("MyStruct3 c offset : %d\n",offsetof(struct MyStruct3,c));    // 偏移8
printf("MyStruct4 b offset : %d\n",offsetof(struct MyStruct4,b));    // 偏移2
printf("MyStruct5 e offset : %d\n",offsetof(struct MyStruct5,e));    // 偏移16
printf("MyStruct6 c offset : %d\n",offsetof(struct MyStruct6,c));    // 偏移4
printf("MyStruct7 c offset : %d\n",offsetof(struct MyStruct7,c));    // 偏移
system("pause");
return 0;
}


测试代码
文中所用的windows为windows7 64位, gcc版本为:gcc version 5.1.0 (tdm64-1);linux为CentOSLinux release 7.2.1511 (Core),gcc版本是gcc version 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC)

参考文章:https://en.wikipedia.org/wiki/Data_structure_alignment
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: