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

C语言,内存对齐,内存分配,地址操作,结构体(一)

2014-04-11 18:29 351 查看
今天看到别人一个C语言的基础题,突然之间脑袋空了,很长时间都没有用过C语言,也很长时间没有想过这种问题了,每天code,code、、、却把基础忘记的干干净净,以后还是多写点笔记,脑袋记不住就用文字记,谁让咱笨呢。

下面结构体所占内存字节数是多少?先声明一下,我的系统是64位windows系统

struct student{

      int num;

      char name[10];

      char sex;

      int age;

      float score;
}
还是先看一下机子上的数据类型占多少个字节?这个很让人头疼。先用gcc编译器试一下

printf("%d\n",sizeof(int));
	printf("%d\n",sizeof(short));
	printf("%d\n",sizeof(char));
	printf("%d\n",sizeof(float));
	printf("%d\n",sizeof(double));
	printf("%d\n",sizeof(long));

gcc编译器结果:4, 2, 1, 4, 8, 4。float占4个字节。

现在查看一下struct字节大小

struct student stu;
	printf("%d\n", sizeof(stu));
输出结果:24
简单分析一下,首先肯定的是编译器和操作系统在分配内存的时候为了提高计算机系统的访问效率进行了内存对齐,先回顾一下为什么要内存对齐

内存对齐是为了提高CPU的访问效率。对齐通常是以一个机器字为准,如果一个结构或者类的总长度未满机器字(通常是4byte)的整数倍,就需要进行内存对齐。机器进行数据读取时需要从4字节的边界读取,所以不定义在4字节边界的数据就需要两次读取。比如:如果一个整数定义在4字节边界上就可以在一个时钟周期内读出,否则就需要用两个时钟周期。

举个简单的例子:大家都知道一个byte是8个bit,而现在流行的32位机指的是一次可以存取32个bit,也就是4个byte,在这种情况下,最有效率的作法当然是一次读4个byte。也就是即便你只取一个byte的内容,实际上,机器一次也是取了4个byte,然后把其中的一个byte给你。当然取4个byte并不是随机组合的,而是按照一定的次序,比如一次取0、1、2、3四个单元的内容,下次访问就是4、5、6、7。由此,如果你的数据恰好在0、1、2、3,则机器只需访问一次,就可以把所有的内容取出来,然而,如果你的数据跨越了这个边界,比如在2、3、4、5,机器在第一次访问的时候,只能取出2、3的内容,还需要进行一次访问才能将4、5的内容取出。如此一来,必须进行两次访问才能取出,所以效率当然会降低。

当然现在计算机发展的基本上已经可以忽略这个影响,而且访问的时候也不简简单单就是按照上面的来进行的,操作系统的水很深,有兴趣慢慢研究,我也不懂、、、什么数据不同步,如何不同步了,硬件底层知识很多、、、,深一点讲,对齐就不是效率问题了,不对齐是可能带来错误,异常的。给个帖子地址:好多不懂,http://bbs.csdn.net/topics/30388330

查看一下结构体变量地址

printf("%x\n",&stu.num);
	printf("%x\n",&stu.name);
	printf("%x\n",&stu.sex);
	printf("%x\n",&stu.age);
	printf("%x\n",&stu.score);
	
	printf("==============\n");
	printf("%d\n",sizeof(stu.num));
	printf("%d\n",sizeof(stu.name));
	printf("%d\n",sizeof(stu.sex));
	printf("%d\n",sizeof(stu.age));
	printf("%d\n",sizeof(stu.score));


结果如下:
28fec8
	28fecc
	28fed6
	28fed8
	28fedc
	==============
	4
	10
	1
	4
	4

结构体变量地址占用情况

num :28fec8----28fecb,4个字节

name :28fecc----28fed5,10个字节

sex :28fed6----28fed7,1个字节

age :28fed8----28fedb,4个字节

score :28fedc----28fedf,4个字节

可以看出,age并没有从d7开始分配,而是从d8开始分配的,再给name分配内存时为了对齐,不是分配的10个字节,而是按照4的整数倍12个字节分配,同时分配sex时发现,name空闲的两个字节>sex的大小1个字节,于是将sex放到了name的后面,age则从d8开始分配。实在结构体分配的空间是4+12+4+4=24

现在做点改动,将sex定义成char数组

struct student{

      int num;

      char name[10];

      char sex[2];

      int age;

      float score;
};
从新编译一下:
28fec8
	28fecc
	28fed6
	28fed8
	28fedc
	==============
	4
	10
	2
	4
	4
	==============
	24
结构体变量地址占用情况
num :28fec8----28fecb,4个字节

name :28fecc----28fed5,10个字节

sex :28fed6----28fed7,2个字节

age :28fed8----28fedb,4个字节

score :28fedc----28fedf,4个字节

结构体实际分配内存4+12+4+4=24
再修改一下结构体

struct student{

      int num;

      char name[10];

      char sex[3];

      int age;

      float score;
};
从新编译一下:
28fec4
	28fec8
	28fed2
	28fed8
	28fedc
	==============
	4
	10
	3
	4
	4
	==============
	28
现在结构体是28个字节,结构体只不过比上面那个多了一个字节,但是这个字节到了下一个单元块,因此需要多分配一个4字节存储
结构体内存分配:

num :28fec4----28fec7,4个字节

name :28fec8----28fed1,10个字节

sex :28fed2----28fed4,3个字节

age :28fed8----28fedb,4个字节

score :28fedc----28fedf,4个字节

结构体实际内存分配:4+16+4+4=28个字节,给那么分配完10个字节后,发现剩下的两个字节不够存储sex,于是又多分配了4个字节,值得注意的是,再给name分配完内存之后,并没有从下一个单元块开始给sex分配内存,而是直接在name之后将内存分配给了sex,是name+sex+【】【】【】,而不是name+【】【】+sex+【】。
假如现在在name之后不是char数组,而是int型数据,内存分配时则会在下一个存储块上分配内存,按照name+【】【】+int来分配内存。

最后再来观察一下结构体的地址,score的地址不变的,一直是28fedc,当我们修改结构体时,前面的地址会变,但是score的地址没变(当然这个不是绝对的)。在分配内存的时候先给score分配的,然后逐个分配前面的成员变量。

score的地址要高于num的地址,因此分配内存的时候是从高地址向低地址生长的。上面的结构体应该存储在栈区,地址生长方向由高向低生长。(当然这个也不是绝对的)

成员变量的首地址一直是偶数(这个基本上都是这样)。

最后在主函数定义几个变量,查看一下变量的地址:

struct student stu;
	
	int i;
	int j;
	double k;
变量地址如下:
28fec4
28fec0
28febc
28feb0
变量的地址是依次降低的,先定义的变量地址高
现在在k前面添加一个变量

struct student stu;
	
	int i;
	int j;
	int m ;
	double k;
输出地址
printf("%x\n",&stu);
	printf("%x\n",&i);
	printf("%x\n",&j);
	printf("%x\n",&k);
	printf("%x\n",&m);
结果如下:
28fec4
28fec0
28febc
28feb028feb8
当然,如果重启一下机子,上面的结果就变了,还会出现地址按变量定义的顺序由高向低生长
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: