C语言中内存对齐规则讨论(struct)

对齐:

现代计算机中内存空间都是按着byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就是需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

对齐的作用:

各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存储。其他平台可能没有这种情况,但是最常见的是如果不按照合适其平台的要求对数据进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶数地址开始,如果一个int型(假设为32位)如果存放在偶数开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次独处的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。

对齐的实现

通常我们写程序的时候,不需要考虑对齐问题,编译器会替我们选择适合目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对制定数据的对齐方法。缺省情况下,编译器为结构体的每个成员按其自然対界条件分配空间。各个成员按照他们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。自然対界即默认对齐方式,是指按结构体的成员中size最大的成员对齐。

最常见的就是struct数据结构的sizeof的结果出乎意料。

结构体的sizeof的值并不是简单的将其中各个元素所占的字节相加,而是要考虑到存储空间的字节对齐问题

结构体默认的字节对齐准则:

1.           结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

2.           结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字符;

3.           结构体的总大小为结构体最宽基本类型成员大小和编译器缺省対界条件大小中比较小得那个值的整数倍,如有需要编译器会在最后一个成员之后加上填充字节;

一 结构体长度的求法:

A.     成员都相同时(或含数组且数组数据类型同结构体其他成员数据类型)

结构体长度=成员数据类型长度*成员个数;

结构体中数组长度=数组数据类型长度*数组元素个数;

B.     成员不同

第一步: 结构体的首地址有系统自动分配我们不予考虑

第二步:计算第一个成员变量的大小

第三步:计算第二个成员变量的大小,此时要注意此成员变量的偏移量(距离结构体首地址的长度)要保证是此变量大小的整数倍,如果不够则补空位;

依次计算所有成员变量,并求和。

第四步:选出所有成员变量中长度最长的变量的值,此时要保证总和是此变量长度的整数倍。如果不是则在最后面补空位

注意:结构体作为成员时,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。

例题一、

struct  test1

{

char a;

int  b;

double c;

bool d;

};

则sizeof(test1)值为24

内存结构为 1***  1111  11111111  1*******(其中*为补空位)

例题二、

struct  test2

{

char a;

struct test1  bb;

int  c;

};

则sizeof(test2)的值为40

首先求a大小为1,在求bb时我们需要考虑偏移量,此时我们使用的bb的对比值并不是24而是test1中的最长值8,因此在字符a后需要补空位7位

然后加上bb长度24,

再计算c并加上其长度4.此时一共长36.

最后我们要注意原则中的第三条。在test2中最长的是结构体类型bb中的double,故总长度应该是8的整数倍。所以最后补位4位,得到40

内存结构为 1********  1***  1111  11111111  1*******  1111****(其中*为补空位)

例题三、

Struct test3

{

Char  a;

Int b[4];

};

此时计算sizeof(test3)为20

内存结构为1***  1111
1111 1111 1111

二、对齐规则

每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数32位机上是8)。程序员可以通过预编

译命令#pragma
pack(n),n=1,2,4,8,16 来改变这一系数,其中的n 就是你要指定的“对齐系数”。

指定対界:

一般的,可以通过下面的方法来改变缺省的対界条件:

使用伪指令#pragma  pack(n),编译器将按照n个字节对齐;

使用伪指令#pragma  pack(),取消自定义的字节对齐方式;

注意:如果#pragma  pack(n)中指定的n大于结构体中最大的成员的size,则其不起作用,结构体仍然按照size最大的成员进行対界。

规则1:

数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset

为0 的地方,以后每个数据成员的对齐按照#pragma
pack 指定的数值和这个数据成员自身长度中,比较小的那个进行。

规则2:

结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进

行对齐,对齐将按照#pragma
pack 指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

规则3:

结合1、2 颗推断:当#pragma
pack 的n 值等于或超过所有数据成员长度的时候,这个n

值的大小将不产生任何效果。

试验

我们通过一系列例子的详细说明来证明这个规则吧!

我试验用的编译器包括GCC
3.4.2 和VC6.0 的C 编译器,平台为Windows
XP + Sp2。

我们将用典型的struct 对齐来说明。首先我们定义一个struct:

#pragma pack(n)

struct test_t

{

int a;

char b;

short c;

char d;

};

#pragma pack(n)

首先我们首先确认在试验平台上的各个类型的size,经验证两个编译器的输出均为:

sizeof(char) = 1

sizeof(short) = 2

sizeof(int) = 4

我们的试验过程如下:通过#pragma
pack(n)改变“对齐系数”,然后察看sizeof(struct
test_t)的值。

1、1 字节对齐(#pragma
pack(1))

输出结果:sizeof(struct
test_t) = 8 [两个编译器输出一致]

分析过程:

1) 成员数据对齐

#pragma pack(1)

struct test_t {

int a;

char b;

short c;

char d;

};

#pragma pack()

成员总大小=8

2) 整体对齐

整体对齐系数=
min((max(int,short,char), 1) = 1

整体大小(size)=$(成员总大小) 按$(整体对齐系数) 圆整=
8 [注1]

2、2 字节对齐(#pragma
pack(2))

输出结果:sizeof(struct
test_t) = 10 [两个编译器输出一致]

分析过程:

1) 成员数据对齐

#pragma pack(2)

struct test_t {

int a;

char b;

short c;

char d;

};

#pragma pack()

成员总大小=9

2) 整体对齐

整体对齐系数=
min((max(int,short,char), 2) = 2

整体大小(size)=$(成员总大小) 按$(整体对齐系数) 圆整=
10

3、4 字节对齐(#pragma
pack(4))

输出结果:sizeof(struct
test_t) = 12 [两个编译器输出一致]

分析过程:

1) 成员数据对齐

#pragma pack(4)

struct test_t {

int a;

char b;

short c;

char d;

};

#pragma pack()

成员总大小=9

2) 整体对齐

整体对齐系数=
min((max(int,short,char), 4) = 4

整体大小(size)=$(成员总大小) 按$(整体对齐系数) 圆整=
12

4、8 字节对齐(#pragma
pack(8))

输出结果:sizeof(struct
test_t) = 12 [两个编译器输出一致]

分析过程:

1) 成员数据对齐

#pragma pack(8)

struct test_t {

int a;

char b;

short c;

char d;

};

#pragma pack()

成员总大小=9

2) 整体对齐

整体对齐系数=
min((max(int,short,char), 8) = 4

整体大小(size)=$(成员总大小) 按$(整体对齐系数) 圆整=
12

5、16 字节对齐(#pragma
pack(16))

输出结果:sizeof(struct
test_t) = 12 [两个编译器输出一致]

分析过程:

1) 成员数据对齐

#pragma pack(16)

struct test_t {

int a;

char b;

short c;

char d;

};

#pragma pack()

成员总大小=9

2) 整体对齐

整体对齐系数=
min((max(int,short,char), 16) = 4

整体大小(size)=$(成员总大小) 按$(整体对齐系数) 圆整=
12  

[注1]

什么是“圆整”?

举例说明:如上面的8 字节对齐中的“整体对齐”,整体大小=9 按4 圆整=
12

圆整的过程:从9 开始每次加一,看是否能被4 整除,这里9,10,11 均不能被4 整除,到12 时可以,则圆整结束。

C语言中内存对齐规则讨论(struct)的更多相关文章

  1. C语言中内存对齐方式

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

  2. c语言中内存对齐问题

    在最近的项目中,我们涉及到了“内存对齐”技术.对于大部分程序员来说,“内存对齐”对他们来说都应该是“透明的”.“内存对齐”应该是编译器的“管辖范围”.编译器为程序中的每个“数据单元”安排在适当的位置上 ...

  3. C语言中内存对齐与结构体

    结构体 结构体是一种新的数据类型,对C语言的数据类型进行了极大的扩充. struct STU{ int age; char name[15]; }; struct STU a; //结构体实例 str ...

  4. C语言中内存对齐

    今天一考研同学问我一个问题,一个结构体有一个int类型成员和一个char类型成员,问我这个结构体类型占多少个字节,我直接编个程序给他看结果.这个结构体占八个字节,咦,当时我蛮纳闷的,一个int类型四个 ...

  5. 【C/C++】内存对齐规则和实战

    内存对齐规则和实战 这篇文章是我的平时的一个笔记修改后来的.这里主要介绍一下内存对齐的规则,以及提供一些实战一下.几篇我觉得比较好的详细的介绍内存对齐的作用什么的博文会在文末附上. 规则 在开始实战前 ...

  6. C语言中内存的管理

    一.动态内存分配与释放 1.为什么要使用动态内存分配,以下看一个实例,关于超市中购买记录的一段程序 #include <stdio.h> #include <string.h> ...

  7. C语言中内存分配 (转)

    在任何程序设计环境及语言中,内存管理都十分重要.在目前的计算机系统或嵌入式系统中,内存资源仍然是有限的.因此在程序设计中,有效地管理内存资源是程序员首先考虑的问题. 第1节主要介绍内存管理基本概念,重 ...

  8. 【转】C语言中内存分配

    原文:C语言中内存分配 在任何程序设计环境及语言中,内存管理都十分重要.在目前的计算机系统或嵌入式系统中,内存资源仍然是有限的.因此在程序设计中,有效地管理内存资源是程序员首先考虑的问题. 第1节主要 ...

  9. C语言中内存分配

     C语言中内存分配   在任何程序设计环境及语言中,内存管理都十分重要.在目前的计算机系统或嵌入式系统中,内存资源仍然是有限的.因此在程序设计中,有效地管理内存资源是程序员首先考虑的问题. 第1节主要 ...

随机推荐

  1. sys.dm_exec_query_stats的total_worker_time的单位是微秒还是毫秒

    该视图sys.dm_exec_query_stats存放的就是当前所有执行计划的详细信息,比如某条执行计划共占CPU多少等等.因为该视图对编译次数.占用CPU资源总量.执行次数等都进行了详细的记录,所 ...

  2. webapi IHttpActionResult无引用和config.MapHttpAttributeRoutes()无引用解决办法

    1. 打开NuGet,打开方法 工具->库程序包管理器->程序包管理器控制台,如下图所示: 2. 输入如下命令Install-Package Microsoft.AspNet.WebApi ...

  3. Opencl 学习笔记

    1. HelloWorld

  4. babel-plugin-transform-remove-strict-mode

    场景:在VUE项目中,需要用到横向滚动条,在引入MUI相关的组件后,模板中的代码如下 在控制台中报错 报错内容说的是在严格模式下(strict mode)类型错误 经过推测,觉得可能是mui.js中用 ...

  5. Win10删除或是不显示快速访问中最近使用文件记录

    Win10删除或不显示快速访问中最近使用文件记录 安装win10系统后,在文件资源管理器的快速访问将默认记录使用和访问了电脑的一些文件,但是有些最近访问文件的历史纪录,并不想让别人看到,所以就想快速删 ...

  6. [Selenium3+python3.6]自动化测试2-入门

    参考http://www.cnblogs.com/yoyoketang/p/6123890.html #coding=utf-8 #Import webdriver time module from ...

  7. arduino安装出现驱动程序不适用于该平台

    之前重新安装了系统,然后重新安装arduino驱动的时候出现了之前没遇到过的问题,这里记录一下. 现在装的是win7 64位的系统,先去官方下载(官方下载慢的,可以去相关论坛下载),有安装版和解压版的 ...

  8. linux 服务器常规巡检并生成报表(一)

    背景 最近接到一个需求要求每天巡检各台业务设备,并导出报表,但一想到设备有N台,一台台每天巡检这样的重复劳作实在是太伤神了,因此决定写一个脚本来搞定这件事. 首先,第一个要解决的问题是批量服务器执行命 ...

  9. linux(centos)下安装supervisor进程管理工具

    在接触supervisor进程管理工具之前,使用springboot打包部署到linux服务器的流程是这样子的,如下图所示: 上图展示的就是最一般的流程,如果项目是小项目或者demo可以这样子去部署, ...

  10. webpack中使用WebpackDevServer实现请求转发

    index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...