C语言----字节对齐

news2024/12/22 17:41:11

一:字节对齐的概念

        针对字节对齐,百度百科的解释如下:

        字节对齐是字节按照一定规则在空间上排列,字节(Byte)是计算机信息技术用于计量存储容量和传输容量的一种计量单位,一个字节等于8位二进制数,在UTF-8编码中,一个英文字符等于一个字节,字节按照一定规则在空间上排列就是字节对齐。

        上面就提到按照一定规则,那规则是什么,按什么规则去对齐,带着这个疑问往下走

二:为什么要字节对齐

        我们为什么要进行字节对齐,不对齐会有什么后果,在计算机中我们任何一个动作无非就是保证程序的正确性,提高程序的性能和可靠性。

        1,平台的硬要求,必须要字节对齐

        某些平台对特定类型的数据只能从特定地址开始存取,而不允许其在内存中任意存放。例如,Motorola 68000 处理器不允许16位的字存放在奇地址,否则会触发异常,因此在这种架构下编程必须保证字节对齐。

        2,提高程序的性能

        字节对齐如何能提高程序的性能?CPU内部有几个重要的部件决定了CPU一次能处理的字节和可访问的内存大小。寄存器,ALU和数据总线的位数,这些共同决定了CPU的字长,常见CPU的字长有4位,8位,16位,32位和64位。字长越多,则CPU内部硬件规模和造价越高。如果CPU字长是16位。它的寄存器和总线也是16位。那么它一次处理的数据长度就为2字节。

        当访问一个变量时,当该变量的地址为偶地址(即字变量的低字节在偶地址单元,高字节在奇地址单元),则需要一个总线周期访问该字变量;如果该字变量的地址为奇地址(即字变量的低字节在奇地址单元,高字节在偶地址单元),则需要用两个连续的总线周期才能访问该字变量,每个周期访问一个字节。

        字节对齐让CPU读取数据的效果高了,这就解释了为什么字节对齐能提高程序的性能。

        3,节省程序的内存

        下面我们以一个实际的例子来看看字节对齐如何节省内存

#include <stdio.h>
#include <stdlib.h>
 
struct byte1
{
 char a;
 int b;
 short c;
};

struct byte2
{
 char a;
 short c;
 int b;
};

int main()
{
    // please write your code here
   printf("struct byte1 size:%d\n",sizeof(struct byte1));
   printf("struct byte2 size:%d\n",sizeof(struct byte2));
}

 可以看到,同样是存储一个char,一个int,一个short,结构体中顺序不一样,结构体的所占的空间也不一样。之所以出现上述结果,就是因为编译器要对数据成员在空间上进行对齐

三:字节对齐规则

1,基本类型对齐规则

基本类型包括char、int、float、double、short、long等基本数据类型。CPU位数不同所占的字节数也不一样,如下图所示

在这里插入图片描述

         对齐要求:起始地址为其长度的整数倍即可。如,int类型的变量起始地址要求为4的整数倍,char类型的变量只占一个字节,那起始地址放哪都行。

2,结构体对齐规则

1>每个数据成员的起始位置必须是自身大小的整数倍; 

2>结构体总大小必须是结构体成员中最大的对齐模数的整数倍;

3> 结构体包含数组时,按单个类型对齐方式;

4>共用体union取成员的最大内存,但包含在结构体内时,按union内部最大类型字节数的整数倍开始存储;

struct byte1
{
 char a;
 int b;
 short c;
};

结构体大小:12
解释:char占一个字节,int占四个字节,由于int的起始地址要在4的倍数上,char后边补齐3个字节,shor占两个字节,但是整个结构体大小要是最大的对齐模数的整数倍,即4的倍数,所以补两个字节,一共12个字节

struct byte2
{
 char a;
 short c;
 int b;
};

结构体大小:8

解释:char占一个字节,short占两个字节,由于short的起始地址要在2的倍数上,char后边补齐1个字节,int占四个字节,刚好在4的倍数上,所以总共8个字节

那结构体里嵌套结构体呢?

结构体包含另一个结构体成员,则被包含的结构体成员要从其原始结构体内部的最大对齐模数的整数倍地址开始存储(比如struct a里含有struct b,b中有char、double 、int 元素,那么b应该从8(double)的整数倍开始存储)

结构体嵌套共同体

结构体包含共用体成员,则该共用体成员要从其原始共用体内部成员中的最大对齐模数的整数倍地址开始存储

结构体最后包含0数组

struct byte2
{
 char a;
 short c;
 int b;
 double d[0];
};

结构体最后包含0数组,那0数组占空间吗?长度为0的数组的主要用途是为了满足需要可变长度的结构体,具体用法是在一个结构体的最后,申明一个长度为0的数组,就可以使得这个结构体是可变长的。对于编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量,数组名这个符号本身代表了一个不可修改的地址常量。

3,共同体对齐规则

共同体的内存除了取最大成员内存外,还要保证是所有成员类型size的最小公倍数。

union byte3
{
 char a;
 short c[5];
 int b;
};

 共同体byte3中最大成员就是short c[5],占10个字节,由于要保证是所有成员类型size的最小公倍数,即4个倍数,所以是12

4,存在#pragma pack宏的对齐规则

#pragma pack(n)//编译器将按照n个字节对齐

#pragma pack()//取消自定义字节对齐方式

******对齐规则******

结构体、联合、类的结构成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和自身对齐模数中最小的那个。

结构体的大小是#pragma pack指定的数值的整数倍。

#pragma pack(4)
typedef struct 
{
    int age;
    char name[0];
    double a;
} Person;
#pragma pack();//结束#pragma pack(4)对齐。  如果没有结束,aa也按照#pragma pack(4)对齐

typedef struct 
{
    double age;
    Person k;
} aa;


int m=sizeof(Person); // m=12, 按照4字节对齐
int n=sizeof(aa); // n=24,  按照8字节对齐        按照#pragma pack(4)对齐的话,n=20

5,位域字节对齐规则

“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。使用位域的主要目的是压缩存储。

位域列表的形式为: 类型说明符 位域名:位域长度(单位:位 bite)

如:struct bs
      {
           int a:8;
           int b:2;
           int c:6;
       } data;
其中位域a占8位,位域b占2位,位域c占6位。

位域说明:

1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域

2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节(8位)的长度,也就是说不能超过8位二进位。
3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: int :2
 

位域对齐规则

1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;

2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;

3) 如果相邻的位域字段的类型不同从新的存储单元开始,偏移量为其类型大小的整数倍,即不压缩;(各编译器的具体实现有差异,VC6采取不压缩方式,Dev-  

     C++采取压缩方式)

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

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


例题:

(1)typedef struct  AA
{
       unsigned int b1:5;
       unsigned int b2:5;
       unsigned int b3:5;
       unsigned int b4:5;
       unsigned int b5:5;
}AA;

sizeof(AA)= 4 

【解析】参考规则 1)。由于相邻成员类型相同,unsigned int为 4 个字节,b1占5位,b2加上b1的位数之和为10位,不超过4字节,因此b2接着b1继续存储;

      同理b3、b4、b5的类型相同,位数之和不超过4字节,因此接着b2继续存储,总位数为25位。

      由于结构体的大小是最宽类型成员的整数倍,因此25位之后的补0,直到补满4字节。

(2)typedef struct  AA
{
       unsigned int b1:5;
       unsigned int b2:5;
       unsigned int b3:5;
       unsigned int b4:5;
       unsigned int b5:5;
       unsigned int b6:5;
       unsigned int b7:5;
}AA;
   sizeof(AA)= 8 

【解析】参考规则 1) 和规则 2) 。由于相邻成员类型相同,unsigned int为 4 个字节(32位),当存储到 b7 时,b7和b6之前的位数相加超过4字节,

因此b7从新的存储单元开始存储。

即b1~b6 存储在 第0~29位,第30、31位补0,b7从下一个 4字节存储单元 开始存储5位,剩下的补0。

 (3)struct test1

{

char a:1;

char :2;

long b:3;

char c:2;

};

 sizeof(test1)= 12

【解析】 

char a:1; //用一个字节去存储

char :2;  //空域。因为与前面的a的类型相同,而两个位域的位宽相加仍然少于8位,所以依然用1个字节表示

long b:3; //long类型的位宽是4个字节,与前面的char类型不同,所以b与a之间偏移4个字节,它们之间自动补充3个字节 

char c:2; //因为c与b又不同型,以test1中的最长的long类型的位宽进行偏移,所以虽然char只用1个字节就够了,但依然要占4个字节。

结构体总长以最长的类型位宽做为偏移量,最长的是long型,占4位,所以不同类型之间应该是4个字节的偏移,即test1应该是4字节的整数倍。 

总共是12字节。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/840537.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PS设计技巧01

大部分切图工作都是在PS中完成的 1、如何得知宽度和高度&#xff0c;先截个图&#xff1a; 2、找个文件夹&#xff0c;把截取的图片放进去&#xff0c;然后ctrl o 把图片文件放进去 3、存入图片&#xff0c;我们用的是网页图片&#xff0c;用的是ctrl r键&#xff0c;调出标尺…

记录电赛色块追踪部分

代码其实也很简单&#xff0c;我只不过加入了按键控制暂停、蜂鸣器、led和如何控制追踪的效果&#xff08;调PID&#xff09;。B站的那些大神早早地完成了要求&#xff0c;我犯了一个不好地错误&#xff0c;企图三连让他们分享思路&#xff0c;这是不对的&#xff0c;电赛本身的…

python之prettytable库的使用

文章目录 一 什么是prettytable二 prettytable的简单使用1. 添加表头2. 添加行3. 添加列4. 设置对齐方式4. 设置输出表格样式5. 自定义边框样式6. 其它功能 三 prettytable在实际中的使用 一 什么是prettytable prettytable是Python的一个第三方工具库&#xff0c;用于创建漂亮…

谷粒商城第九天-解决商品品牌问题以及前后端使用检验框架检验参数

目录 一、总述 二、商品分类问题 三、前端检验 四、后端检验 五、总结 一、总述 在完成完商品分类的时候&#xff0c;后来测试的时候还是发现了一些问题&#xff0c;现在将其进行解决&#xff0c;问题如下&#xff1a; 1. 取消显示的时候&#xff0c;如果取消了显示&…

Unity学习参考文档和开发工具

☺ unity的官网文档&#xff1a;脚本 - Unity 手册 ■ 学习方式&#xff1a; 首先了解unity相关概述&#xff0c;快速认识unity编辑器&#xff0c;然后抓住重点的学&#xff1a;游戏对象、组件|C#脚本、预制体、UI ☺ 学习过程你会发现&#xff0c;其实Unity中主要是用c#进行开…

Javaweb学习(2)

Javaweb学习 一、Maven1.1 Maven概述1.2 Maven简介1.3、Maven基本使用1.4、IDEA配置Maven1.6、依赖管理&依赖范围 二、MyBatis2.1 MyBatis简介2.2 Mybatis快速入门2.3、解决SQL映射文件的警告提示2.4、Mapper代理开发 三、MyBaits核心配置文件四、 配置文件的增删改查4.1 M…

【c++】VSCode配置 c++ 环境(重新制作)

上一篇帖子【c】VSCode配置 c 环境&#xff08;小白教程&#xff09;_vscode配置c/c环境_StudyWinter的博客-CSDN博客 大火&#xff0c;但是依旧有很多小伙伴反应没有配好环境&#xff0c;今天打算重新写一个教程&#xff0c;希望对大家有帮助。 1 MinGW下载安装 在CSDN上传了…

高温环境下光模块光功率降低的原因与解决方案

光模块是光纤通信系统中的关键组件&#xff0c;其稳定的光功率输出对于确保通信质量至关重要。然而&#xff0c;高温环境下光模块的光功率往往会出现下降&#xff0c;本期文章我们将探讨高温环境下光模块光功率降低的原因和解决方案。 一、高温环境下光功率降低的原因 &#…

深入理解@ConfigurationProperties注解的作用和用法

文章目录 前言一、配置文件application.properties二、添加依赖三、创建配置类四、测试总结 前言 ConfigurationProperties是一个用于定义属性的注解&#xff0c;通常用于Spring应用程序的配置类中。 通过使用ConfigurationProperties注解&#xff0c;可以将外部配置文件中的属…

YOLOv5引入FasterNet主干网络,目标检测速度提升明显

目录 一、背景介绍1.1 目标检测算法简介1.2 YOLOv5简介及发展历程 二、主干网络选择的重要性2.1 主干网络在目标检测中的作用2.2 YOLOv5使用的默认主干网络 三、FasterNet简介与原理解析3.1 FasterNet概述3.2 FasterNet的网络结构3.2.1 基础网络模块3.2.2 快速特征融合模块3.2.…

ENSP-PPP Chap双向认证

日期6-26 &#x1f4ce;PPP chap 双向认证.zip &#x1f4ce;PPP chap 双向认证.docx

Linux常用命令——domainname命令

在线Linux命令查询工具 domainname 显示和设置系统的NIS域名 补充说明 domainname命令用于显示和设置系统的NIS域名。 语法 domainname(选项)(参数)选项 -v&#xff1a;详细信息模式&#xff1b; -F&#xff1a;指定读取域名信息的文件。参数 NIS域名&#xff1a;指定要…

ClickHouse(十一):Clickhouse MergeTree系列表引擎 - MergeTree(1)

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术&#xff0c;IT贫道_Apache Doris,Kerberos安全认证,大数据OLAP体系技术栈-CSDN博客 &…

通义千问7B本地部署的实战方案

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

Flink开发环境准备: centos-jdk8

linux-jdk8 - Flink开发环境准备 一、基本介绍二、环境准备1.1 JDK环境1.2 开发工具1.3 Maven环境 三、flink下载安装配置3.1 Flink下载3.2 flink本地模式安装 - linux3.3 常用配置3.4 日志的查看和配置 四、单机 Standalone 的方式运行 Flink 一、基本介绍 Flink底层源码是基于…

架构,性能和游戏 《游戏编程模式》学习笔记

开新坑&#xff0c;准备把《游戏编程模式》这本书啃完。这是一本讲设计模式的书&#xff0c;针对游戏开发而作&#xff0c;写得很好。 以下是读书笔记&#xff0c;文末有原文链接 每个程序都有一定的软件架构&#xff0c;哪怕是全塞到main里也是一种架构好的架构可以把代码写成…

【LeetCode 75】第二十题(2215)找出两数组的不同

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码运行结果&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 找出两个数组中不一样的数,将nums1中有的数而nums2没有的数放在res[0]中,将nums2中有的数二nums1没有的数放在res[1]中. 那我们…

路由器静态路由实验

介绍 静态路由是由管理员手动配置和维护的路由。 如何理解静态路由了&#xff1f;配置一条静态路由就是告诉路由器怎么去往某个目的ip或ip地址段。去往目的地的时候需要查找路由表。 静态路由优缺点&#xff1a; 优点&#xff1a; 静态路由配置简单&#xff0c;并且无需动态…

03.利用Redis实现缓存功能---解决缓存穿透版

学习目标&#xff1a; 提示&#xff1a;学习如何利用Redis实现添加缓存功能解决缓存穿透版 学习产出&#xff1a; 缓存穿透讲解图&#xff1a; 解决方案&#xff1a; 采用缓存空对象采用布隆过滤器 解决方案流程图&#xff1a; 1. 准备pom环境 <dependency><gro…

完整模型的训练套路

从心所欲 不逾矩 天大地大 皆可去 一、官方模型的初使用 使用VGG16模型 VGG模型使用代码示例&#xff1a; import torchvision.models from torch import nndataset torchvision.datasets.CIFAR10(/cifar10, False, transformtorchvision.transforms.ToTensor())vgg16_true …