| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 695 人关注过本帖
标题:关于内存对齐的一点个人浅显的理解
只看楼主 加入收藏
我菜119
Rank: 10Rank: 10Rank: 10
等 级:青峰侠
帖 子:938
专家分:1756
注 册:2009-10-17
结帖率:98.26%
收藏
已结贴  问题点数:20 回复次数:4 
关于内存对齐的一点个人浅显的理解
前几天在看c语言中的预处理的时候突然遇到了内存对齐的问题,所以就恶狠狠的补习了一下,同时在咱们这个论坛也看见了有些坛友也发表过对内存对齐的帖子.
从中午一直睡到了三点多钟,这会起来终于算是看完了内存对齐这块知识点,本人是一个菜鸟!!!!

先来介绍一下为什么要进行内存对齐呢??
不想借用什么书上的理解,只是我个人的理解,内存对齐就是为了方便cpu访问数据的效率快即占用的时间少,至于为什么cpu要这样去访问数据,这个要去问设计电脑的人吧!可能有些人认为内存中的数据是按照一个一个单一的字节组成的,其实并不是这样的,内存中的数据是按照一块一块来存储的,
如图:
图片附件: 游客没有浏览图片的权限,请 登录注册
   
块的大小可以是2,4,8......个字节大小,因此CPU在读取内存时是一块一块进行读取的。

假设cpu每次读取的内存快是四个字节的话:

当该数据是从0字节开始时,很CPU只需读取内存一次即可把这4字节的数据完全读取到寄存器中。

当该数据是从1字节开始时,问题变的有些复杂,此时该int型数据不是位于内存读取边界上,这就是一类内存未对齐的数据

此时CPU先访问一次内存,读取0—3字节的数据进寄存器,并再次读取4—5字节的数据进寄存器,接着把0字节和6,7,8字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器。对一个内存未对齐的数据进行了这么多额外的操作,大大降低了CPU性能
因此这就要求数据要按照指定的字节对齐即内存对齐!

内存对齐可以是默认的内存对齐字节值,也可以是什么指定的内存对齐字节值,那我们是如何来指定内存对齐字节值的呢??可以通过#pragma pack(value)
来实现,
比如:
#pragma pack( 4 )
struct s1
{   
    double ddal;
    char c;
    int  tx;
};
struct s2
{
    char c1;
    struct s1 c;
    int tb;
};
#pragma pack()
这段代码就指定了内存对齐字节值为4,也就是cpu每次访问的块的字节值为4。

接下来介绍四个基本的概念:
1) 每个类型的数据都有自己的对齐字节值:
  char类型的对齐字节值为:sizeof( char ) = 1;
  int类型的对齐字节值为: sizeof( int ) = 4;
  short类型的对齐字节值为: sizeof( short int ) = 2;
  float类型的对齐字节值为:sizeof( float ) = 4;
  double类型的对齐字节值为:sizeof( double ) = 8;
  long类型的对齐字节值为:sizeof( long ) = 4;
 
2) 结构体或者是联合体或者是类的对齐字节值是该成员中有效对齐字节值中
   最大的那个;

3) 指定对齐字节值:#pragma pack( value ) 时的指定对齐字节值为value;

4) 对于指定的对齐字节值,每个类型的对齐字节值是本身对齐字节值与指定对齐字节值
   中最小的那个;
像上面的那段代码每个结构体中的变量的对齐是怎么回事的呢?
这个是一个完整的程序:
#include <stdio.h>

#define MSG ( unsigned int )( void * )

#pragma pack( 4 )
struct s1
{
    double ddal;
    char c;
    int  tx;
};
struct s2
{
    char c1;
    struct s1 c;
    int tb;
};
#pragma pack()

int
main()
{
    struct s1 a;
    struct s2 b;
    printf( "ddal = %p  c = %p tx = %p " ,
        MSG &a.ddal - MSG &a ,
        MSG &a.c - MSG &a ,
        MSG &a.tx - MSG &a );
    printf( "\n" );
    printf( "c1 = %p\ns1.ddal = %p\ns1.c = %p\ns1.tx = %p\ntb = %p\n" ,
        MSG &b.c1 - MSG &b ,
        MSG &b.c.ddal - MSG &b,
        MSG &b.c.c - MSG &b ,
        MSG &b.c.tx - MSG &b ,
        MSG &b.tb - MSG &b );
    return 0;
}
那么输出值是多少的呢?
让我们来分析一下吧!
我们必须要记住对于结构体中的成员的有效对齐字节值是:指定对齐字节值与自身对齐字节值中的最小的那个!!!
这样我们就可以分析一下了呀!有点磨叽了吧??呵呵!
struct s1
{
    double ddal;
    char c;
    int  tx;
};
1)在这个结构体中double 类型的ddal变量的有效对齐字节值为:4

其起始地址也就是该结构体的起始地址:Ox00000 0000 这样能够保证Ox00000 0000%4 = 0;
2)char 类型的c变量的有效对齐字节值为 :1
同时要保证其地址对其有效字节值的取余的值要为零,所以它的地址要从 Ox0000 0008开始,这样就保证了 Ox0000 0008%1 = 0;
3)int类型的tx变量的有效对齐字节值为:4
其地址为:Ox0000 000c (在这里可能有人会觉得是Ox0000 0009,但是Ox0000 0009%4的值能够等于0吗???显然不能够,所以tx的地址值应保证对4取余要满足为:0,因此只能够从12开始,也就是Ox0000 000c了呀!)

然后我们再来看一下第二个结构体吧!
struct s2
{
    char c1;
    struct s1 c;
    int tb;
};
在这个结构体中还嵌套了另一个结构体,那么嵌套的这个结构的对齐字节值是多少呢呀?这个需要说明一下,它的内存对齐字节值是该结构体中的所有成员的有效内存字节值中最大的那个,显然它的内存对齐字节值应该是:
4
1) char类型的c1变量的有效内存对齐字节值为:1
所以其地址是:Ox0000 0000
2) double 类型的ddal的变量的有效内存对齐字节值是:4
所以其地址是:Ox000 0004
3) char类型的c变量的有效内存对齐字节值是:4
所以其地址是:Ox0000 000c
4) int类型的tx变量的有效内存对齐字节值是:4
所以其地址是:Ox0000 0010
5) int类型的tb变量的有效内存对齐字节值是:4
所以其地址是:Ox0000 0014

以上就是本人一些浅显的理解了!如果有问题希望共同探讨!

[ 本帖最后由 我菜119 于 2010-9-18 17:52 编辑 ]
搜索更多相关主题的帖子: 内存 
2010-09-18 17:51
jack10141
Rank: 11Rank: 11Rank: 11Rank: 11
来 自:陕西西安
等 级:小飞侠
威 望:6
帖 子:706
专家分:2271
注 册:2010-8-10
收藏
得分:3 
一起学习下!!!

Coding就像一盒巧克力,你永远不会知道你会遇到什么BUG
别跟我说你是不能的,这让我愤怒,因为这侮辱了你的智慧
2010-09-18 18:45
meishaoqing
Rank: 2
等 级:论坛游民
帖 子:6
专家分:10
注 册:2010-9-18
收藏
得分:3 
呵呵,讲的不错,学习了
2010-09-18 20:21
真我
Rank: 4
等 级:业余侠客
威 望:1
帖 子:146
专家分:210
注 册:2010-7-14
收藏
得分:3 
说得不错,内存对齐就是硬件的需要
2010-09-18 20:21
vandychan
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
等 级:贵宾
威 望:18
帖 子:2296
专家分:6418
注 册:2010-8-20
收藏
得分:3 
LZ好厉害啊

到底是“出来混迟早要还”还是“杀人放火金腰带”?
2010-09-18 21:02
快速回复:关于内存对齐的一点个人浅显的理解
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.017899 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved