C语言结构体的内存对齐问题
在C语言开发当中会遇到这样的情况:
#include <stdio.h> struct test {
int a;
char b;
}; int main(int argc, const char * argv[])
{
printf("%lu\n", sizeof(struct test));
return ;
}
sizeof操作输出的结果是8,可是int和char类型的长度加起来只有5,那么为什么输出了8呢?
这就牵扯到结构体的内存对齐问题,事实上,结构体中的变量在内存当中并不是以一种连续紧凑的方式存储的。
上面这个例子是一种最简单的情况,来看一下它的内存排布:
0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | 0xA | 0xB | 0xC | 0xD | 0xE | 0xF | |
0x10 | a | b | 空位 | |||||||||||||
0x20 |
可见,从0x15到0x18都是空位,为什么要这样存储呢?因为计算机字长为4,处理结构体变量时,需要一次读入32位,也就是4个字节,那么假定两个结构体变量a和b,a从0x10开始,b从0x15开始的话,则需要两次寻址,第一次是读出0x14到0x17,取出后三位,第二次是从0x18到0x1B,取出第一位,然后做位运算,由于变量处理很频繁,这样就增加了性能的开销,那么只能采取用空间换时间的策略了。
假设有两个结构体变量,那么内存排布是这样的:
0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | 0xA | 0xB | 0xC | 0xD | 0xE | 0xF | |
0x10 | a | b | 空位 | a | b | 空位 | ||||||||||
0x20 |
这便是内存对齐的第一个方面——结构体的末尾如果没有对齐,则会留出空位来,为排在它前后的变量提供寻址上的便利。
事实上,结构体的大小,必然是其中占用空间最大的类型的长度的整数倍。
接下来改变一下我们的结构体:
struct test {
char b;
int a;
};
它的长度同样是8,内存排布如下:
0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | 0xA | 0xB | 0xC | 0xD | 0xE | 0xF | |
0x10 | b | 空位 | a | |||||||||||||
0x20 |
同样基于上面提到的原因,如果将a放在0x11的位置,需要两次寻址。那么,结构体内存排列的一个重要原则就是要得到最少的寻址次数,显然,为了方便找到int型的a,需要在b后面空出3个字节的空间来。
下面举一个稍微复杂一点的例子:
struct test {
char a;
int b;
char c;
char d;
double e;
char f;
};
它的长度是32,看一下内存排列:
0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | 0xA | 0xB | 0xC | 0xD | 0xE | 0xF | |
0x10 | a | 空位 | b | c | d | 空位 | ||||||||||
0x20 | e | f | 空位 |
为了让变量b寻址方便,在a的后面空出了3个字节,而c和d无论如何都要寻址一次,所以顺序排下去就可以了,而e长度为8,按说可以从0x1C开始排,但是这里需要和类型本身的长度8进行对齐,则排在了0x20的位置,于是乎d的后面又空出了6个字节的空间,最后f排在e的后面,之后为了结构体本身和最长类型的长度8对齐,后面又留出了7个字节的空位。
显而易见结构体当中变量的顺序是会影响存储空间的,在设计结构体的时候需要进行优化,比如将上面的结构体改写成下面的形式可以节省空间:
struct test {
char a;
char c;
char d;
char f;
int b;
double e;
};
这种排列只占用16字节,内存如下:
0x0 | 0x1 | 0x2 | 0x3 | 0x4 | 0x5 | 0x6 | 0x7 | 0x8 | 0x9 | 0xA | 0xB | 0xC | 0xD | 0xE | 0xF | |
0x10 | a | c | d | f | b | e | ||||||||||
0x20 |
可见,内存对齐是一种空间换时间的手段,当然,为了节约内存,可以通过如下代码令结构体紧凑排列:
#pragma pack(1)
C语言结构体的内存对齐问题的更多相关文章
- C语言 结构体的内存对齐问题与位域
http://blog.csdn.net/xing_hao/article/details/6678048 一.内存对齐 许多计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地 ...
- 3.c语言结构体成员内存对齐详解
一.关键一点 最关键的一点:结构体在内存中是一个矩形,而不是一个不规则形状 二.编程实战 #include <stdlib.h> #include <stdio.h> stru ...
- C语言结构体在内存中的存储情况探究------内存对齐
条件(先看一下各个基本类型都占几个字节): void size_(){ printf("char类型:%d\n", sizeof(char)); printf("int类 ...
- C语言结构体及其内存布局
code[class*="language-"], pre[class*="language-"] { color: rgba(51, 51, 51, 1); ...
- C语言结构体的字节对齐原则
为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据 ...
- C语言结构体变量字节对齐问题总结
结构体字节对齐 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题.从理论上讲,对于任何 变量的访问都可以从任何地址开始访问,但 ...
- C语言结构体占用空间内存大小解析
结构体的数据类型的有点我们就不啰嗦了,直接来看相同数据结构体的几种书写的格式吧. 格式一: 01.struct tagPhone 02.{ 03. char A; 04. int ...
- c语言结构体在内存中存储,字节对齐
注意: 出于效率的考虑,C语言引入了字节对齐机制,一般来说,不同的编译器字节对齐机制有所不同,但还是有以下3条通用准则: (1)结构体变量的大小能够被其最宽基本类型成员的大小所整除: (2)结构体每个 ...
- C语言结构体变量内存分配与地址对齐
地址对齐简单来说就是为了提高访问内存的速度. 数组的地址分配比较简单,由于数据类型相同,地址对齐是一件自然而然的事情. 结构体由于存在不同基本数据类型的组合,所以地址对齐存在不同情况,但总体来说有以下 ...
随机推荐
- 雅虎工程师初始化css
/*css reset code */ /**** 文字大小初始化,使1em=10px *****/ body { font-size:62.5%; } /* for IE/Win */ html&g ...
- jdbc和数据库的应用
jdbc是Java Data Base Connectivity(java数据库连接): 是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和 ...
- [每日一题] 11gOCP 1z0-052 :2013-09-4 block header grows............................................A33
转载请注明出处:http://write.blog.csdn.net/postedit/11100311 正确答案是:AD 要理解这道题就要去了解数据块的结构.引用OCPPPT中的一幅图: 从这幅图中 ...
- 多线程程序 怎样查看每个线程的cpu占用
可以用下面的命令将 cpu 占用率高的线程找出来: ps H -eo user,pid,ppid,tid,time,%cpu,cmd --sort=%cpu 这个命令首先指定参数'H',显示线程相关的 ...
- 用函数式的 Swift 实现图片转字符画的功能
今天整理 Pocket 中待看的文章,看到这篇<Creating ASCII art in functional Swift>,讲解如何用 Swift 将图片转成 ASCII 字符.具体原 ...
- Html5移动端页面布局通用模板暨移动端问题总结
最近的移动端项目终于告一段落了,其中遇到了不少问题,在此和大家总结分享. 首先,说一下结构.一般的手机页面大致可以分为五块:head, content, foot,solidbar,dialog. 针 ...
- 你所不了解的css选择器
我们目前接触到的选择器:.class #id div ...... 不了解的选择器:a>b a+b [a~=b] [a|=b]...... 一下说举5 6 7 8为css3中的定 ...
- Java中的事务——JDBC事务和JTA事务
Java中的事务——JDBC事务和JTA事务 转载:http://www.hollischuang.com/archives/1658 之前的事务介绍基本都是数据库层面的事务,本文来介绍一下J2EE中 ...
- activiti笔记二:用户任务
1, assignee 代替humanPerformer 功能 2, cadidateUsers代替potentialOwner功能 3, candidateGroups代替potentialOwn ...
- 使用DatagramSocket与DatagramPacket传输数据
参考传智播客毕向东JAVA视频. 注: DatagramSocket发送的每个包都需要指定地址,而Socket则是在首次创建时指定地址,以后所有数据的发送都通过此socket. A socket is ...