首页 > 其他分享 >自定义类型结构体♪♪♪

自定义类型结构体♪♪♪

时间:2024-03-23 17:32:11浏览次数:26  
标签:自定义 int 成员 位段 内存 类型 对齐 结构

目录

结构体

结构体的声明

结构体的变量的定义

结构体的变量的初始化

结构成员访问操作符

结构体的特殊声明(匿名结构体)

结构体重命名

结构体内存对齐

对齐规则:

为什们要存在内存对齐?

怎样设计结构体使其占用内存空间大小尽量小一点?

修改默认对齐数

结构体传参

结构体实现位段

位段是什么

位段的使用 

为什们存在位段

 位段的内存分配

位段的跨平台问题

位段的应用

位段使用的注意事项


结构体

C语言已经给我们提供了内置类型即C语言自带的数据类型(如char short int double)用来描述某些变量,但这是远远不够的,当我们要描述一本书的时候,需要描述书的价格,作者,出版日期等等,描述一个学生的时候需要描述学生的姓名,学号,性别,家庭住址等等,单一的内置类型远远不够我们描述这些有多重属性的事物,所以C语言给我们提供了自定义类型用来让程序员方便描述这些变量,让程序员可以根据需要来自定义某些事物

结构体的声明

struct tag
{
        member-list;
}variable-list;

其中,tag是结构体的标签

member-list 是结构体的成员,可以是一个或多个

variable-list 是定义的结构体变量,可以在声明的同时定义一个或多个结构体变量

应该注意结构体声明的大括号外面必须要以分号结尾,不管有没有定义变量

//结构体类型的声明
struct Book//结构体标签
{
	char name[30];//书名
	char author[20];//作者
	int number;//书号
	double price;//价格
}s1,s2;//声明的同时定义的结构体变量

结构体的变量的定义

Ps:定义在main函数外面的是全局变量,定义在main函数里里面的是局部变量

结构体的变量的初始化

struct Stu
{
    char name[20];//名字
    int age;//年龄
    char sex[5];//性别
    char id[20];//学号
};

int main()
{
    //按照结构体成员的顺序初始化
    struct Stu s = { "张三", 20, "男", "20230818001" };
    printf("name: %s\n", s.name);
    printf("age : %d\n", s.age);
    printf("sex : %s\n", s.sex);
    printf("id : %s\n", s.id);

    //按照指定的顺序初始化
    struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex = "⼥ };
    printf("name: %s\n", s2.name);
    printf("age : %d\n", s2.age);
    printf("sex : %s\n", s2.sex);
    printf("id : %s\n", s2.id);
    return 0;
}

结构成员访问操作符

当我们要访问结构成员的时候有两种方法,一种是直接访问,另外一种是间接访问

如果是结构成员则可以通过操作符( . )直接访问

使用方式:结构体变量 . 成员名

有时候我们得到的不是⼀个结构体变量,而是得到了⼀个指向结构体的指针,这时候通过 ( -> )来间接访问

使用方式:结构体指针 -> 成员名

结构体的特殊声明(匿名结构体)

注意:匿名结构体是结构体的一种特殊形式,它省去了结构体标签名 tag ,因此匿名结构体类型只能在声明的时候使用一次,所以匿名结构体变量只能在声明的同时定义

匿名结构体的初始化:

在定义的同时初始化

mian函数里面初始化

匿名结构体类型只能在声明的时候使用一次

 

编译报了一个警告,等号两边类型兼容,说明编译器把它俩当成了这个不同的类型,所以我们在使用匿名结构体类型声明的时候只能用一次

结构体重命名

结构体内存对齐

我们初步了解了结构体,那当我们想要知道结构体类型的大小,这是用sizeof求结构体的大小时,我们会惊奇的发现结构体类型的大小不等于它所有成员变量大小的总和

为什么呢?

这就与结构体在内存中的存储方式有关了

结构体在内存中存储有一个对齐规则

对齐规则:

  • 结构体的第一个成员存储必须对齐到与结构体起始地址的0偏移量地址处
  • 其他成员必须对齐到该成员对齐数的整数倍处
  • 对齐数 == 当前成员大小与默认对齐数的较小值
  • VS默认对其数 == 8          Linux中gcc无默认对齐数,对齐数为该成员大小
  • 结构体总大小要对齐到所有成员对齐数的最大值的整数倍处
  • 当结构体嵌套使用时,被嵌套的结构体要对齐到它的所有成员中的最大对齐数的整数倍处,结构体总大小要对齐到最大对齐数的整数倍处(包含被嵌套的结构体对齐数大小)

现在就可以分析为什么时12个字节了

为什们要存在内存对齐?

1.平台原因 (移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定
类型的数据,否则抛出硬件异常。
2. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对⻬的内存,处理器需要
作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地
址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以
用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两
个8字节内存块中
总体来说:结构体的内存对齐是拿空间来换取时间的做法

怎样设计结构体使其占用内存空间大小尽量小一点?

尽可能的把占用内存空间较小的成员集中在一起

修改默认对齐数

通过 #pargma pack(n)  预处理指令来修改默认对齐数大小,n 为修改后的大小

通过 #pargma pack()    指令回复默认对齐数

结构体传参

传值调用

传址调用

所以在结构体传参时,尽量传递地址,节省时间和空间上面的开销

结构体实现位段

位段是什么

位段,位即是二进制位,是C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 利用位段能够用较少的位数存储数据

  • 位段的成员只能是int    unsigned int    signed int 等类型,大部分平台还支持char类型
  • 位段的成员名后边有⼀个冒号和⼀个数字

位段的使用 

struct S
{
    int _a:2;//其中冒号后面的数字代表的是该成员在内存空间中占几个比特位
    int _b:5;
    int _c:3;
    int _d:4;
};

位段是基于结构体实现的 ,位段的成员后面要接一个冒号一个数字,最后以分号结尾,数字代表该成员在内存空间中占几个比特位

为什们存在位段

信息的读取通常是以字节为单位,但有时某些信息的存储不足1个或多个字节时,就会造成内存空间浪费,例如表示真假,结构成员的取值范围就只有0和1,而0和1最多占用1个比特位,成员的类型用的是int ,占32个字节,就会有31比特位空间浪费掉

 位段的内存分配

  • 位段的空间通常是以成员的类型每次开辟1 / 4个字节大小,int 就一次开辟4个字节,char 就开辟1个字节
  • VS上面,位段每次开辟的空间都是从高地址向低地址分配的
  • 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

位段的跨平台问题

  • int位段被当成有符号数还是无符号数是不确定的
  • 位段中最大位的数目不能确定(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
  • 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  • 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的

位段的应用

在网络协议中使用位段,可以有效缓解网络拥堵

位段使用的注意事项

因为位段内存分配的特殊,位段成员起始地址不一定能在某个字节的起始位置上,内存中每个字节分配一个地址,⼀个字节内部的bit位是没有地址,所以不能对位段成员进行取地址操作,scanf也就不能对某个成员进行取地址输入值,如果要给成员赋值,先把值给中间变量,在把中间变量的值赋值给位段成员

struct A
{
    int _a : 2;
    int _b : 5;
    int _c : 10;
    int _d : 30;
};
int main()
{
    struct A sa = {0};
    scanf("%d", &sa._b);//这是错误的
    //正确的⽰范
    int b = 0;
    scanf("%d", &b);
    sa._b = b;
    return 0;
}

标签:自定义,int,成员,位段,内存,类型,对齐,结构
From: https://blog.csdn.net/2301_80424145/article/details/136920471

相关文章

  • 【Godot4自学手册】第二十七节自定义状态机完成看守地宫怪物
    本节,我将使用自定义状态机实现看守地宫怪物,完成了基础类State,状态机类StateMachine的编码,实现了怪物的闲置巡逻类、追踪类和攻击类,以及对应动画等。这节代码有点多,不过还好,代码比较简单。最终效果如下:一、基本概念状态机(StateMachine)是有限状态自动机的简称,是指一个数学......
  • python 内置数据结构-数值型
    内置数值型数据结构int整数(int):在Python中,整数是没有小数部分的数字。整数可以是正数、负数或零。Python中的整数没有大小限制,取决于内存区域的大小,可以表示任意大小的整数。x=10y=-5z=0print(x,y,z)#输出:10-50float浮点数(float):浮点数是带有小数......
  • 高通平台怎么检测充电器类型为SDP,CDP,DCP
    高通平台(QualcommSnapdragon)检测充电器类型SDP(StandardDownstreamPort,标准下行端口)、CDP(ChargingDownstreamPort,充电下行端口)和DCP(DedicatedChargingPort,专用充电端口)是基于USBBatteryChargingSpecification1.2(USBBC1.2)或更高版本的规定实现的。这些充电类型主要是通......
  • lua/c开发:lua增加自定义require方式
    我们会有需要自定义加载模块逻辑的需求,比如支持从自定义格式数据包中加载一个被加密过的lua文件的方式,这在生产环境中非常常见,可以有效保护源码同时保持整洁;lua模块管理库会从若干个loader中逐个尝试加载模块,lua原生提供了4个loader;staticconstlua_CFunctionsearchers[]={......
  • 鸿蒙自定义控件实现罗盘数字时钟效果
    前言:DevEcoStudio版本:4.0.0.600关注过我的小伙伴一定知道我之前写过一篇基于Android的 仿抖音效果的数字时钟罗盘 最近看了鸿蒙的Canvas组件,今天通过Canvas组件也实现下罗盘数字时钟的效果。参考链接:OpenHarmonyCanvas  OpenHarmonyCanvasrenderingcontext2d效果:......
  • 使用Django-Simple-Captcha在Django项目加入验证码模块并自定义样式
    在Django项目中加入验证码功能,通常需要借助第三方库,比如Django-Smple-Captch、Django-reCAPTCHA、DEF-reCAPTCHA、Wagtail-Django-ReCaptcha、Django-Friendly-Captcha等。其中,Django-Smple-Captcha是一个流行的选择,它提供了一个简单而强大的Django应用,无需调用第三方API,......
  • 高通充电类型
    高通(Qualcomm)在Android平台上提供了多种充电技术,以下是几种主要的充电类型:1.QuickCharge(QC)QuickCharge1.0:最初的快速充电技术,提升了充电速度。QuickCharge2.0:引入了动态电压和电流调整(HVDCP),能够提供更高的充电功率。QuickCharge3.0:增加了INOV(最佳电压智能协商),提高......
  • 可持久化数据结构
    前言可持久化数据结构是一些很厉害的东西,大家可以去看lxl的课件和她在WC2024上的讲课,由于能力限制,这个博客会讲得比较基础。这里应该有个题单。根据lxl的课件,我们可以把其分为几类:部分可持久化:允许访问历史版本,不能修改历史版本完全可持久化:允许把目前版本替换为历史......
  • java数据结构与算法基础-----排序------堆排序
    java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846文章目录堆排序是利用堆(数据结构)设计的排序算法,属于选择排序,最坏,最好,平均时间复杂度均为O(nlogn),不稳......
  • 深度解析webpack5以及打包实践攻略,看完这篇带你玩转高级自定义打包
    1.webpack5对比webpack4做了哪些优化Webpack5对比Webpack4存在一些重要的优化。Webpack5在性能、构建速度、TreeShaking等方面都有所改进:性能改进:Webpack5在构建速度和性能方面有所提升。这主要是通过改进缓存策略、优化构建算法以及增强的持久化缓存等方式......