C语言之内存字节对齐
2016-04-30 18:28
381 查看
1.结构体的内存大小
比如下面这段代码:#include<stdio.h> #include<stdlib.h> struct arr { int a; int b; }; int main() { printf("%d", sizeof(struct arr)); system("pause"); return 0; }
我们可以知道这最终输出为8,当然,struct的大小是每个成员大小的和,所以4+4就是8。
如果你按照上面这样理解下面的这一段代码:
#include<stdio.h> #include<stdlib.h> struct arr { int a; char c; int b; char d; }; int main() { printf("%d", sizeof(struct arr)); system("pause"); return 0; }
你认为输出会是多少呢?是上面那样4+1+4+1=10吗?
答案是输出为16,这里,就牵扯到了一个叫做内存字节对齐的问题
上述结构体,在内存中的存储方式,其实就可以这样理解:
采用内存对齐的方式,使得我们查找信息更加方便,缺陷是浪费了一些空间。
其实字节对齐的细节和具体编译器实现相关,但一般而言,满足下面的三条准则。
字节对齐的三条准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;例如上面第二个结构体变量的地址空间。
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
所谓最宽基本类型是指像char、short、int、float、double这样的内置数据类型。“数据宽度”就是指其sizeof的大小。诸如结构体、共用体和数组等都不是基本数据类型。
对上述三条准则的理解:
#include<stdio.h> #include<stdlib.h> struct arr { char a; char arr[11]; }; int main() { printf("%d", sizeof(struct arr)); system("pause"); return 0; }
在这里我们可以知道,这里的struct占12个字节,12可以整除1,所以结果就是12。在这最宽基本类型为char ,最终的结果为12,是1个的整数倍。
#include<stdio.h> #include<stdlib.h> struct arr { short a; char arr[11]; }; int main() { printf("%d", sizeof(struct arr)); system("pause"); return 0; }
这里呢,最宽基本类型是short,占2个字节,所以内存在存储时候,将是下面这种情况:
这里我们11+2无法整除,所以,这里我们多开辟一块空间,11+2+1整除2。
所以我们可以得到最终的结果就是14。
结果14,是最宽基本类型short的整数倍。
所以,对于最宽基本类型就相当于对内存的存查大小的一个限定。所以后续的操作你也可以根据上述的分析一样分析。
对于第二条的理解:
#include<stdio.h> #include<stdlib.h> struct ar { char ch; int a; short s; }; int main() { printf("%d\n", sizeof(struct ar)); }
在这里,进行偏移时,假设结构体的地址为300500,ch地址300200,这时候0可以被1整除,那么偏移到a的地址就是300204,这时候300504-300500就可以整除4,到s偏移为300208,这时候就可以整除4,所以总共这时候是12个字节。
二对于下面这种方式:
#include<stdio.h> #include<stdlib.h> struct ar { char ch; short s; int a; }; int main() { printf("%d\n", sizeof(struct ar)); }
在这里假设结构体地址300200,那么首先ch地址为300200,这时候300200-300200可以整除1,接下来偏移,300201不可以整除2,所以再向后偏移到300202,然后一直便宜到300204,300204可以被4整除,所以这里总共8个字节。
例:
#include<stdio.h> #include<stdlib.h> struct ar { char ch; short s; double a; int num; char arr[19]; }; int main() { printf("%d\n", sizeof(struct ar)); system("pause"); }
这个例子中,我们可以按照上面的方式来进行分析,假设结构体首地址200200,那么ch就是200200-200200=0,0可以整除1,然后接下来是short,200201-200200不可以整除2,所以接下来,要根据对齐,补齐,所以偏移到200202,然后下面200204-200200不能整除8,所以偏移至200208,接下来200216-200200可以整除4,所以偏移至200216,然后200220-200200整除1,可以整除,偏移至200220,接下来要考虑最宽数据类型,现在所占的字节数为8+8+4+19,所以填充一个字节,最终结果为40个字节大小。
2.共用体的内存对齐
对于共用体,也是一样的,也遵守结构体三个规则,共用体的大小也必须是最宽基本类型大小整数倍。如:
#include<stdio.h> #include<stdlib.h> union ar { char ch; short s; double a; }; int main() { printf("%d\n", sizeof(union ar)); system("pause"); }
最宽数据类型为double,所以,输出最终为8。
如:
#include<stdio.h> #include<stdlib.h> union ar { int ch; char arr[13]; }; int main() { printf("%d\n", sizeof(union ar)); system("pause"); }
这里依然,需要最后的结果是最宽数据类型的整数倍,因为最大的变量为arr,长度为13,然后补齐3个字节,所以最终的结果也就是16。
水平有限,如有错误,请大家都指出来。
相关文章推荐
- 练习题c++(一)
- C++中istream的使用
- C++中istream的使用
- C语言错误: HEAP CORRUPTION DETECTED
- C++读取Excel 精华
- C++ 访问控制(public,protected,private)
- 【c++】istream 转为 string
- C++学习笔记60——模板编译模型
- c#调用c++制作的基于mfc的ocx控件
- 基本单链表的增删改C++
- 数组&字符串&结构体&共用体&枚举
- c语言中的段和内存四区
- C++定义一个对象和new一个对象的区别与联系
- 【C++ STL系列】迭代器 iterator
- 《More Effective C++》读书笔记-操作符
- 通过表达式构造二叉树 c++
- C++中的istringstream、ostringstream、stringstream
- C++单例的创建与使用
- c语言中的位移位操作
- C语言链表的基本操作