一、x86

总体上遵循两个原则:

  • 整体空间----占用空间最大的成员(的类型)所占字节数的整数倍
  • 对齐原则----内存按结构成员的先后顺序排列,当排到该成员变量时,其前面已摆放的空间大小必须是该成员类型大小的整倍数,如果不够则补齐,以此向后类推
说明:假定结构体是从地址0开始依次存放各个变量的

struct s1
{ 变量占据内存位置 去掉余下变量后结构体所占内存空间
char a; //0 //
double b; //8-15 //
int c; //16-19     //
char d; //20      //
short e; //22-23      //
short f; //24-25      //
} student;

详细解释:sizeof()用法汇总

为什么会有这样的规定呢?

这一定与处理器的字长有关(处理器一次存取数据的宽度)和编译器对结构体变量的处理有关。不幸的是,本人对x86架构不甚熟悉,只能借助与ARM结构来说明这个问题。

二、ARM

总体上遵循两个原则:

  • 整体空间----如果含有>4字节类型的成员,整体空间是4字节数的整数倍;反之,都是<=4字节的成员,占用空间最大的成员类型所占字节数的整数倍
  • 对齐原则----内存按结构成员的先后顺序排列。当排到该成员变量时,倘若该成员>4字节,其前面已摆放的空间大小必须是4的整倍数;倘若该成员<=4字节,其前面已摆放的空间大小必须是该成员类型大小的整倍数,如果不够则补齐。以此向后类推

说明:假定结构体是从地址0开始依次存放各个变量的
struct s1
{ 变量占据内存位置 去掉余下变量后结构体所占内存空间
char a;     //0    //
double b;     //4-11   //
int c;     //12-15   //
char d;     //16    //
short e;     //18-19    //
short f;     //20-21    //
}student;

为什么会有这样的规定呢?

这一定与处理器的字长有关(处理器一次存取数据的宽度),所以必须先将ARM的字长,实际上涉及的内容是load/stort存储方式。ARM字长是32位,4个字节。也就是说,无论如何它都要使用32位数据总线(虽然它也支持字节/半字传送)。

ldr指令

什么意思呢?看程序你就知道了。

int类型变量的存储

    AREA    Init,CODE,READONLY
IMPORT main ENTRY _entry
ldr r0,=0x12345678
ldr r1,=0x1000
str r0,[r1] ldr r2,[r1] ;r2=0x12345678
ldr r2,[r1,#] ;r2=0x123456 不对齐发生旋转
ldr r2,[r1,#2] ;r2=0x1234 bl main
END

试想,倘若我们定义了一个int变量,值为0x12345678,按照小端格式在0x1000、0x1001、0x1002、0x1003,依次存放的数据是0x78、0x56、0x34、0x12,而我们再从这儿(0x1000)取的时候,还是0x12345678。

假设我们按照小端格式存但是没有对齐(4字节对齐),在0x1001、0x1002、0x1003、0x1004,依次存放0x78、0x56、0x34、0x12,再假设0x1000单元存了一个0xab。那么我们再从这儿(0x1001)取的时候,取出来的就是0xab345678,显然读到的不是之前存的数据。

就算是,有一个非常聪明的编译器,知道如果没有对齐存放的话,将来取的时候,要从0x1001、0x1002、0x1003取一部分(一条指令),然后再从0x1004取一部分(一条指令),最后整合(好几条指令),这样的工作实在是麻烦,编译器的效率是极低的。

所以,最好的办法就是一开始存数据的时候,就根据其类型合适的对齐存放。例如int变量,就给它分配到能被4整除的地址上(实际上在它之前的存储空间大小就是4的倍数),而不要将其分配在不能被4整除的位置上。

倘若是double类型的变量,实在无可奈何,存的时候只有分两次存,读的时候分两次读,这也是32位机最快的方法。倘若没有对齐,不知道要在存取时折腾多少次。也就是说,把double类型(其实还有其他>4字节的类型)都是放在以4为倍数的地址上。

char型变量的存储

至于char型变量,是没什么要求的。

   ldr r0,=0x12345678
ldr r1,=0x1000
str r0,[r1] ldrb r2,[r1] ;r2=0x00000078
ldrb r2,[r1,#] ;r2=0x00000056
ldrb r2,[r1,#2] ;r2=0x00000034

ldrb指令,你从哪个地方读,就返回你想要的值,不会发生什么移位旋转的问题。所以,你把一个char型变量,放在任意位置都行,ldrb指令都能准确无误的将其取出。

short变量的存储

还有short变量,这个也是有说唱的。情况也有些复杂,但没有ldr指令那么复杂。

    ldr r0,=0x12345678
ldr r1,=0x1000
str r0,[r1] ldrh r2,[r1] ;r2=0x00005678
ldrh r2,[r1,#] ;r2=0x00005678 不对齐读的还是0x10000的内容
ldrh r2,[r1,#] ;r2=0x00001234

试想,倘若我们定义了一个short变量,值为0x5678,按照小端格式在0x1000、0x1001,依次存放的数据是0x78、0x56,而我们再从这儿(0x1000)取的时候,还是0x00005678。

假设我们按照小端格式存但是没有对齐(2字节对齐),在0x1001、0x1002 ,依次存放0x78、0x56,再假设0x1000单元存了一个0xab。那么我们再从这儿(0x1001)取的时候,取出来的就是0x000078ab,显然读到的不是之前存的数据。

所以在存储short变量时,是存在以2为倍数的地址上。

回到正题

由上可知,我们知道了变量在存储过程中对地址的限制。通常,这些非常底层的东西,程序员是无需知道的。只不过,当用到struct结构体时,会把这个问题翻出来。

结构体变量的成员是按次序在内存中排放,排放时候也需要遵从上边的限制。现在,那两条规则的原因就是这样了。

结论

不同架构的处理器对应着一定的编译器,这些不同的编译器对struct变量的处理是不一样的。

union 与struct的空间计算的更多相关文章

  1. C语言union关键字,union和struct区别

    union 关键字的用法与struct 的用法非常类似. union 维护足够的空间来置放多个数据成员中的“一种”,而不是为每一个数据成员配置空间,在union 中所有的数据成员共用一个空间,同一时间 ...

  2. 用union 和 struct 位域操作

    很久没有用C 语言中的 union 和 struct 位域操作了. 最近用了一下(当然,我承认是从stackoverflow 上抄的) 需求是这样的,已知一个 LPARAM 整数 3866625 ,求 ...

  3. (数据科学学习手札84)基于geopandas的空间数据分析——空间计算篇(上)

    本文示例代码.数据及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 在本系列之前的文章中我们主要讨论了g ...

  4. (数据科学学习手札88)基于geopandas的空间数据分析——空间计算篇(下)

    本文示例代码及数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 在基于geopandas的空间数据分析系列 ...

  5. union 和struct大小计算

    一.字节对齐 现代计算机的内存空间是按照字节(byte)来划分的,字节对齐的意思是在给特定变量类型分配内存空间的时候,变量的内存地址是它本身变量类型大小的整数倍.比如,给int类型的变量a分配地址空间 ...

  6. union和struct的区别之处,在于内存的共享上

    首先看看union,在c++中,union可能没有多大用处,在c语言中,可能我们要借助其完成很多巧妙的设计,下面是其一个完整的定义: union UTest         {             ...

  7. [转]C++中sizeof(struct)怎么计算?

    版权属于原作者,我只是排版. 1. sizeof应用在结构上的情况 请看下面的结构: struct MyStruct{ double dda1; char dda; int type;}; 对结构My ...

  8. C++中sizeof(struct)怎么计算?(转)

    struct为空时,大小为1. 1. sizeof应用在结构上的情况 请看下面的结构: struct MyStruct { double dda1; char dda; int type; }; 对结 ...

  9. union与struct以及大小端

    两者的区别: 1. 共用体和结构体都是由多个不同的数据类型成员组成, 但在任何同一时刻, 共用体只存放了一个被选中的成员, 而结构体的所有成员都存在.   2. 对于共用体的不同成员赋值, 将会对其它 ...

随机推荐

  1. mac下教你如何开源项目托管GitHub

    自从google code关闭了下载服务了之后,GitHub作为了目前最好用的免费开源项目托管站点,众多开源项目都托管在github,其中不乏著名的播放器MPC-HC. 这里教大家如何把代码库上传到G ...

  2. JSP和servlet结合案例

    [使用JSP技术实现猜数字游戏] 设计一个web app,有一个随机数字,给5次机会让客户猜测这个数字: 1)如果客户猜的数字比产生的数字值大,则提示"可惜,大了点". 2)如果客 ...

  3. GWT用frame调用JSP

    Frame htmlFrame = new Frame("../OurHome/modules/core/mainIndex.jsp?merchantId="+merchantId ...

  4. easyui常用控件及样式收藏

    CSS类定义: div easyui-window                               window窗口样式 属性如下: 1)       modal:是否生成模态窗口.tru ...

  5. cookie记录用户名和密码

    getAttribute和getParameter的区别: request.getAttribute():是request时设置的变量的值,用request.setAttribute("na ...

  6. 聊聊 iOS 中的网络加密

    介绍下 公司的接口一般会两种协议的,一种HTTP,一种HTTPS的,HTTP 只要请求,服务器就会响应,如果我们不对请求和响应做出加密处理,所有信息都是会被检测劫持到的,是很不安全的,客户端加密可以使 ...

  7. CentOS7上Nginx的使用

    Nginx 的启动 指定配置文件的方式启动nginx # nginx -c /etc/nginx/nginx.conf 对于yum安装的nginx,使用systemctl命令启动 # systemct ...

  8. Tcp抓包以及tcp状态解释

    tcp三次握手 发送端发送一个SYN=1,ACK=0标志的数据包给接收端,请求进行连接,这是第一次握手:接收端收到请求并且允许连接的话,就会发送一个SYN=1,ACK=1标志的数据包给发送端,告诉它, ...

  9. .net+easyui系列--datagrid

    加载CSS <link href="../../Public/easyui/SiteEasy.css" rel="stylesheet" type=&qu ...

  10. web前端:html

    一.理解表单的作用 1.web 应用程序不仅仅是给用户显示数据,还应该给用户提供一个可以输入数据的图形用户界面.表单的主要作用在于在网页上提供一个图形用户界面,已采集和提交用户输入的数据. 2.htm ...