前两天参加了360测试实习生的笔试,碰到了一个有关c语言内存对齐的题目,回来后实现了一下,下面是代码:

 #include <stdio.h>
#include <stdlib.h>
//#pragma pack(2)
struct s1{
int a;
char b;
char c;
double d;
};
struct s2{
char b;
int a;
char c;
};
struct s3{
char b;
char c;
int a;
};
struct s4{
char b;
int a;
short c;
};
int
main(void){
printf("%d %d\n",sizeof(int),sizeof(char));
struct s1 s1;
printf("s1(int, char, char, double): %d, %lx, %lx, %lx, %lx\n",sizeof(struct s1),&s1.a,&s1.b,&s1.c,&s1.d);
struct s2 s2;
printf("s2(char, int, char): %d, %lx, %lx, %lx\n",sizeof(struct s2),&s2.b,&s2.a,&s2.c);
struct s3 s3;
printf("s3(char, char, int): %d, %lx, %lx, %lx\n",sizeof(struct s3),&s3.b,&s3.c,&s3.a);
struct s4 s4;
printf("s4(char, int, short): %d, %lx, %lx, %lx\n",sizeof(struct s4),&s4.b,&s4.a,&s4.c);
return ;
}

结果如图:

下面就是理论知识了。

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况并非如此。一些平台对某些特定类型的数据只能从某些特定地址开始存取,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放。这就是所谓的字节对齐。字节对齐是为了提高CPU的读取效率。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据.显然在读取效率上下降很多。

编译器对对齐值的选择:

1.数据类型自身对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int、float类型,其自身对齐值为4,单位字节。
2.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
3.指定对齐值:#pragma pack (value)时的指定对齐值value。
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。

在c语言中数据结构的对齐就更复杂些了,windows平台和Linux平台又有些不同。

Win32平台下的微软C编译器(cl.exe for 80×86)的对齐策略:
(1)结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。
       (2)结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
        为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
       (3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要,编译器会在最末一个成员之后加上填充字节(trailing padding)。
        结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。

在GCC中,对齐模数的准则是:对齐模数最大只能是4,也就是说,即使结构体中有double类型,对齐模数还是4,所以对齐模数只能是1,2,4。而且在上述的三条中,第2条里,offset必须是成员大小的整数倍,如果这个成员大小小于等于4则按照上述准则进行,但是如果大于4了,则结构体每个成员相对于结构体首地址的偏移量(offset)只能按照是4的整数倍来进行判断是否添加填充。

如果结构体中含有位域(bit-field),那么VC中准则又要有所更改:
        (1)如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
        (2)如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
        (3)如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;

  (4)如果位域字段之间穿插着非位域字段,则不进行压缩;

  (5)整个结构体的总大小为最宽基本类型成员大小的整数倍。

C语言内存对齐(2)的更多相关文章

  1. C语言内存对齐详解

    一.字节对齐基本概念 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型 ...

  2. C语言内存对齐详解(2)

    接上一篇:C语言内存对齐详解(1) VC对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式.VC 中提供了#pr ...

  3. C语言内存对齐详解(3)

    接上一篇:C语言内存对齐详解(2) 在minix的stdarg.h文件中,定义了如下一个宏: /* Amount of space required in an argument list for a ...

  4. C语言内存对齐原理

    一.什么是字节对齐,为什么要对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这 ...

  5. c语言内存对齐问题

    #include <stdio.h>#pragma pack(4)struct stu{char a;short b;int c;char d;};int main(){printf(&q ...

  6. C语言内存对齐

    转:http://blog.csdn.net/embeddedman/article/details/7429976 首先由一个程序引入话题:  1 //环境:vc6 + windows sp2 2  ...

  7. C语言内存对齐对则

    这篇文章讲的非常好  :  http://blog.csdn.net/hairetz/article/details/4084088 用空间换时间, 规则 : 每个数据成员存储的起始位置都要是它的整数 ...

  8. 对C语言内存对齐的初步了解

    在解释内存对齐的作用前,先来看下内存对齐的规则: 1. 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身 ...

  9. go语言内存对齐

    内存对齐 为保证程序顺利高效的运行,编译器会把各种类型的数据安排到合适的地址并占用合适的长度,这就是内存对齐 每种类型的对齐值就是他的内存边界 64位 类型 对齐边界 (对齐值) int8 1byte ...

随机推荐

  1. Big ball of Mud

    Big ball of Mud 第一种死法:Big ball of Mud 架构里最常用的反面案例是 big ball of mud.很大程度上可以说打格子,把复杂的系统拆解成小格子是架构师最重要的工 ...

  2. VS公布 错 到文件失败 复制到

    他自己和构建网站 ASP.MVC4 最近更改写功能 自此从未公布 已经报道 错 15 到文件失败 easyui\themes\gray\images\Thumbs.db  拷贝到 obj\Releas ...

  3. SQL开发中容易忽视的一些小地方(四)

    原文:SQL开发中容易忽视的一些小地方(四) 本篇我想针对网上一些对于非聚集索引使用场合的某些说法进行一些更正. 下面引用下MSDN对于非聚集索引结构的描述. 非聚集索引结构: 1:非聚集索引与聚集索 ...

  4. CCFadeOut ,CCFadeIn 不能使用的原因

    CCFadeOut *action = CCFadeOut::create(0.5f);  image->runAction(action); 截取部分代码.以上是我写游戏时候遇到的问题代码, ...

  5. Struts2+Spring+Hibernate step by step 11 ssh拦截验证用户登录到集成

    注意:该系列文章从教师王健写了一部分ssh集成开发指南 引言: 之前没有引入拦截器之前,我们使用Filter过滤器验证用户是否登录,在使用struts2之后,全然能够使用拦截器,验证用户是否已经登录, ...

  6. HDU 3037 Saving Beans (Lucas法则)

    主题链接:pid=3037">http://acm.hdu.edu.cn/showproblem.php?pid=3037 推出公式为C(n + m, m) % p. 用Lucas定理 ...

  7. java 获取系统变量(环境变量和环境变量)

    前言 环境变量这个概念不陌生, 就是操作系统的环境变量. 系统变量就是java本身维护的变量. 通过 System.getProperty 的方式获取. 对于不同的操作系统来说. 环境变量的处理可能会 ...

  8. cocos2d-x-2.2的SimpleAudioEngine::sharedEngine()-&gt;playEffect()计划中断bug

    在该计划已经正常,但现在突然发iphone播放声音上就挂了.播放音乐是没有问题的. android没问题. xcode给定的位置,如下面的附图: 网上搜了一下,说是有全局断点造成的.于是command ...

  9. spring中<tx:advice></tx:advice>是什么意思

    <tx:advice id="tv" transaction-manager="transactionManager"> <tx:attrib ...

  10. ORACLE union order by

    select * from ( select a.id,a.oacode,a.custid,a.custname,a.xsz,a.salename,a.communicationtheme,a.com ...