自定义类型:结构体----初学者笔记

news2024/11/26 15:39:27

目录

1. 结构体类型的声明

1.1 结构体类型的简单介绍和声明

1.1.1 结构的声明

1.1.2 特殊的声明

1.1.3 结构的自引用

2. 结构体变量的创建和初始化

3. 结构成员访问操作符

4. 结构体内存对⻬

4.1 对齐规则

4.2 练习

4.2.1 练习1

4.2.2 练习2

4.3 为什么存在内存对⻬?

4.4 修改默认对齐数

5. 结构体传参

6. 结构体实现位段

6.1 什么是位段

6.2 位段的内存分配

6.3 位段的跨平台问题

6.4 位段使⽤的注意事项


1. 结构体类型的声明

1.1 结构体类型的简单介绍和声明


首先,结构体分为内置类型和自定义类型2种,我们今天介绍的就是自定义类型的结构体。
当内置类型不能满足需求的时候,我们就用自定义类型的结构体,例如:我们要描述一本书,不能只通过一个变量来描述,需要知道书的书名、作者、价格、类型等等

1.1.1 结构的声明

而这就需要自定义类型的结构体来实现,结构体里面包含了各种类型的数据,用来描述一个复杂对象的各种属性。

struct book
{
	char name1[20];//作者名
	char name2[20];//书名
	int price;//价格
	//......
};

1.1.2 特殊的声明

我们有时候会看见这种匿名结构体类型
请看接下来的代码:

struct
{
	char a;
	int c;
	float d;
}s = { 0 };
struct
{
	char a;
	int c;
	float d;
}*ps;
int main()
{
	ps = &s;
	return 0;
}
//在上⾯代码的基础上,下⾯的代码合法吗?
ps= &s;

警告:
编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。

1.1.3 结构的自引用

大家觉得下面的代码可以运行成功吗?

struct Node
{
 int data;//存储数据--4个字节
 struct Node next;//下一个节点的地址--->指针类型变量4or8个字节
};
int main()
{
 struct Node n;
 return 0;
}

在给出答案之前,我们先来了解一下数据结构


我们发现不可以,这是为什么呢?
我们可以假想一下:房子里面要放下一个大小一样的房子和另外的东西,这是不可能的
方法:将一个数据结构分成2部分,一半存数据,一半存放下一个节点的地址,这样就可以存储结构体指针了,也就可以结构自引用了

struct Node
{
	int data;//存储数据--4个字节
	struct Node* next;//下一个节点的地址--->指针类型变量4or8个字节
};
int main()
{
	struct Node n;
	return 0;
}

这样问题就解决了

2. 结构体变量的创建和初始化

初始化很简单,请看代码

struct Stu
{
  char name[20];
  int age;
  float score;
}s1,s2;//全局变量
struct Stu
{
  char name[20];
  int age;
  float score;
};

int main
{
  struct Stu s1,s2,s3;//局部变量
  return 0;
}

3. 结构成员访问操作符

结构体访问成员有2种方法:
1.结构体变量.成员变量名
2.结构体指针—>成员变量名

请看代码:

struct Stu
{
	char name[20];
	int age;
	float score;
}s1 = { "zhangsan",22,35.0f };

int main()
{
	struct Stu s2 ={"lisi",19,88.5f };
    struct Stu s3 = { "wangwu",39,76.0f };//按照顺序
    struct Stu s4 = { .age = 33,.name = "xiaofang",.score = 38.5f };//不按顺序
    printf("%s %d %lf\n", s1.name, s1.age, s1.score);
    printf("%s %d %lf\n", s2.name, s2.age, s2.score);
    struct Stu* ps = &s4;
    printf("%s %d %lf\n", ps->name, ps->age, ps->score);
    return 0;
}

4. 结构体内存对⻬

我们先看下面的代码,你觉得输出的是什么?

int main()
{
struct S1
{
 char c1;//1个字节
 int I;//4个字节
 char c2;//1个字节
};
printf("%d\n", sizeof(struct S1));

struct S2
{
 char c1;
 char c2;
 int i;
};
printf("%d\n", sizeof(struct S2));
return 0;
}


结果和你想的一样吗?
为什么成员一样,顺序不一样,存放的数据类型和变量也一样,但是结果不一样呢?
为什么开辟的空间不一样?s1和s2到底是怎么分配内存空间的?这里就涉及到了内存对齐!!!

4.1 对齐规则

1. 结构体的第⼀个成员对⻬到相对结构体变量起始位置偏移量为0的地址处
2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对⻬数 = 编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值
- VS中默认的值为8
- Linux中没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数(不包括默认对齐数),所有对⻬数中最⼤的)的整数倍
4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍

那我们来看看刚刚的s1和s2是怎么存储的


4.2 练习

4.2.1 练习1

struct S3
{
 double d;//8个字节
 char c;//1个字节
 int I;//4个字节
};
printf("%d\n", sizeof(struct S3));


4.2.2 练习2

struct S3
{
 double d;//8个字节
 char c;//1个字节
 int I;//4个字节
};
printf("%d\n", sizeof(struct S3));

struct S4
{
 char c1;
 struct S3 s3;
 double d;
};
printf("%d\n", sizeof(struct S4));

4.3 为什么存在内存对⻬?

简单来说:结构体的内存对⻬是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:

//例如:
struct S1
{
 char c1;
 int i;
 char c2;
};
struct S2
{
 char c1;
 char c2;
 int i;
};

S1 和 S2 类型的成员⼀模⼀样,但是 S1 和 S2 所占空间的⼤⼩有了⼀些区别。

4.4 修改默认对齐数

#pragma 这个预处理指令,可以改变编译器的默认对⻬数。

#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//取消设置的默认对⻬数,还原为默认

但是,我们一般设置成2的次方数,如果乱修改的话,可能适得其反

5. 结构体传参

结构体传参有2种方式,分别是传值调用和传址调用

struct S
{
 int data[1000];
 int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
 printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
 printf("%d\n", ps->num);
}
int main()
{
 print1(s); //传结构体
 print2(&s); //传地址
 return 0;
}

综合来看,还是传址调用比较好

6. 结构体实现位段

6.1 什么是位段

位段是基于结构体的,是可以节省空间的

位指的是二进制位​​​​​​​

位段的声明和结构是类似的,有两个不同:

1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他类型。

2. 位段的成员名后边有⼀个冒号和⼀个数字

使用举例:

struct A
{
 int _a:2;
 int _b:5;
 int _c:10;
 int _d:30;
};
struct B
{
 int _a;
 int _b; 
 int _c;
 int _d;
};
int main( )
{
 printf("%d\n",sizeof(struct A));
 printf("%d\n",sizeof(struct B));
 return 0;
}
//空间是如何开辟的?

后面的数字指的是变量所占的空间大小, 例如:2指的就是变量a占两个二进制位,
以此类推,总共要占到42个二进制位, 我们初步推测,struct A大概需要6个字节

但是通过运行我们发现,实际上需要占到8个字节,但是还是比第二种方法所占内存小

我们发现和猜测的不一样,这就得讲讲位段的内存空间是怎么分配的了

6.2 位段的内存分配

1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型

2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。

3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。

我们以接下来的代码来分析一下:

struct S
{
 char a:3;
 char b:4;
 char c:5;
 char d:4;
};
int main()
{
 struct S s = {0};
 //s.a = 10;
 //s.b = 12;
 //s.c = 3;
 //s.d = 4;
 printf("%d\n",sizeof(struct S));
 return 0;
}

先根据位段大小,开辟空间;再根据存储数据的二进制位读取相应空间位数,存放至内存空间(统一假设从右向左储存,剩余空位不够下一个使用就空着不使用),开辟新的内存空间

下面,我们看看完整代码的内存存储是怎么样的

struct S
{
 char a:3;
 char b:4;
 char c:5;
 char d:4;
};
int main()
{
 struct S s = {0};
 s.a = 10;
 s.b = 12;
 s.c = 3;
 s.d = 4;
 printf("%d\n",sizeof(struct S));
 return 0;
}

为了方便观察,我们在内存中以16进制的形式查看
但是,这是为什么呢?

6.3 位段的跨平台问题

1. int 位段被当成有符号数还是⽆符号数是不确定的。

2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会出问题。

3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃剩余的位还是利⽤,这是不确定的。

总结:

跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

6.4 位段使⽤的注意事项

内存中每个字节分配⼀个地址,⼀个字节内部的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;
}

本次的分享到这里就结束了!!!

PS:小江目前只是个新手小白。欢迎大家在评论区讨论哦!有问题也可以讨论的!

如果对你有帮助的话,记得点赞👍+收藏⭐️+关注➕

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

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

相关文章

知名IT网站博客园陷入绝境

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 博客园陷入生死存亡的绝境。 5月份知名IT开发者网站发布文章称“博客园网站遇到困难了&#xff1a;寻求捐助”&#xff0c;并开通了捐助渠道。4个月过去了&#xff0c;好像效果并不明显&#xff…

婚庆行业类软文怎么写?媒介盒子无偿分享

随着我国经济发展以及对婚礼品质要求的提升&#xff0c;结婚相关的市场发展愈发强劲&#xff0c;由于互联网发展的快速性&#xff0c;大部分新人选择通过网络获取结婚服务信息&#xff0c;所以线上成为婚庆公司的主要获客渠道。而软文营销就是婚庆公司提升品牌形象的主要方式&a…

AI写作工具,智能ai写作工具

在信息化时代&#xff0c;内容创作已经成为了许多行业的核心。从营销广告到新闻报道&#xff0c;从博客文章到学术论文&#xff0c;人们需要不断地产生高质量的文字内容。创作是一项耗时耗力的工作&#xff0c;需要丰富的知识和创造性思维。 AI写作工具&#xff0c;是一类基于人…

docker容器技术实战-3

08 docker 原生网络 原生网络&#xff1a;桥接模式 &#xff1a;生成虚拟网络对 host模式&#xff1a;容器和宿主机共享同一网络栈&#xff0c;不会新建虚拟网卡 none禁用网络&#xff1a;只用回环接口 自定义接口&#xff1a;内嵌dns解析 不同网络之间是被隔离的,默认不能通…

【C++】map与set的封装

文章目录 前言正文1. 类型的泛化2.仿函数3.迭代器3.1正向迭代器3.1.1 3.1.2 - -3.1.3 *3.1.4 ->3.1.5 !完整版代码 4.[]&#xff08;map&#xff09; 框架1.红黑树2.set3.map 总结 前言 在学习了红黑树之后&#xff0c;我们便可以尝试初步的在红黑树的基础上封装出map与set&…

Python日志处理器,同时打印到控制台和保存到文件中,并保证格式一致

使用logging模块的时候&#xff0c;默认是输出到控制台的&#xff0c;当然也可以配置输出到文件中&#xff0c;但是当你配置了文件后&#xff0c;控制台的输出就消失了&#xff0c;所以&#xff0c;需要一个策略即能保存到文件中&#xff0c;又能输出到控制台中。 下面是我做的…

ORB-SLAM2实时稠密地图,解决运行报段错误(核心已转储)运行数据集时出现段错误,出现可视化界面后闪退(添加实时彩色点云地图+保存点云地图)

高翔的稠密建图仓库 1. git clone https://github.com/gaoxiang12/ORBSLAM2_with_pointcloud_map.git 2. 去ORB SLAM2里拷贝Vocabulary到/home/cgm/ORBSLAM2_with_pointcloud_map/ORB_SLAM2_modified文件夹下 3. 删除一些build文件夹 删除ORB_SLAM2_modified/Thirdparty/DB…

国产手机芯片4G方案_紫光展锐安卓核心板虎贲4G智能模块方案定制

元器件清单即BOM物料清单&#xff0c;不同行业领域的BOM表侧重点不一样。安卓主板的BOM表则侧重点在于元器件物料的清单&#xff0c;也就是安卓电路板的PCBA清单&#xff0c;精密的安卓板有上千个物料&#xff0c;可以帮助我们估算物料成本&#xff0c;建立生产计划&#xff0c…

Ftp服务器、 Samba服务器、NFS服务器的区别

根据使用的方式来看&#xff0c;可以分为3种类别的文件服务器&#xff1a;ftp服务器&#xff08;ftp/tftp&#xff09;、 Samba服务器、NFS服务器。ftp的客户可以是任意平台&#xff0c;samba是专门针对windows客户&#xff0c;而NFS则是面向linux/unix用户的。下面是三种服务器…

CRM软件系统价格不同的原因

很多人在了解CRM系统时&#xff0c;发现不同品牌的CRM价格有着很大的区别。一些CRM系统只需要几千块钱&#xff0c;一些CRM系统的报价却要上万&#xff0c;甚至十几万。为什么CRM系统价格不同&#xff1f;下面我们就来说说。 1、功能不同 从功能方面来说&#xff0c;一些CRM系…

OpenCV Series : Slope + Radian + Degree

def lineSlope(p1, p2):slope (p2[1] - p1[1]) / (p2[0] - p1[0])return slopedef slopeAngle(p1, p2):slope lineSlope(p1, p2)radian np.arctan(slope)return radiandef angleDegree(radian):theta int(radian * 180 / np.pi) % 360return theta

【LeetCode热题100】--3.无重复字符的最长子串

3.无重复字符的最长子串 使用滑动窗口&#xff1a; 使用两个指针表示字符串中的某个子串&#xff08;或窗口&#xff09;的左右边界&#xff0c;其中左指针代表着枚举字串的起始位置&#xff0c;而右指针即为 r k r_k rk​在每一步操作中&#xff0c;我们会将左指针向右移动一…

Python类练习

文章目录 题目要求步骤 题目要求 1)创建一个 Kid 类&#xff0c;包含姓名&#xff0c;性别&#xff0c;年龄属性和 play 方法 2) 创建一个 Stu 类&#xff0c;继承 Kid 类&#xff0c;同时包含成绩属性&#xff0c;获取成绩方法&#xff0c;努力学习方法&#xff0c;play方法&…

RocketMQ核心编程模型以及生产环境最佳实践

⼀、回顾 RocketMQ 的消息模型 ⼆、深⼊理解 RocketMQ 的消息模型 1 、 RocketMQ 客户端基本流程 RocketMQ 基于 Maven 提供了客户端的核⼼依赖&#xff1a; 使⽤客户端进⾏编程时&#xff0c;添加这⼀个核⼼依赖就够了。 另外还有⼀个与权限控制相关的核⼼依赖也需要了解。尽…

「聊设计模式」之迭代器模式(Iterator)

&#x1f3c6;本文收录于《聊设计模式》专栏&#xff0c;专门攻坚指数级提升&#xff0c;助你一臂之力&#xff0c;带你早日登顶&#x1f680;&#xff0c;欢迎持续关注&&收藏&&订阅&#xff01; 前言 设计模式是软件开发中经验的总结&#xff0c;是一种被反复…

开学季什么触控笔好用又便宜?推荐平价的电容笔

由于iPad平板的强大功能&#xff0c;才让iPad拥有了更多的用户。拿来画画、记笔记什么的体验都是很不错。但是仅仅是拿来看电视、玩游戏&#xff0c;似乎作用不大。如果你不是想要一支昂贵的苹果电容笔&#xff0c;或者只是想要在日常生活中做笔记&#xff0c;那么你可以考虑一…

springcloud3 分布式事务解决方案seata之TCC模式6

一 TCC模式 1.1 TCC的逻辑 TCC模式与AT模式非常相似&#xff0c;每阶段都是独立事务&#xff0c;不同的是TCC需要人工干预编写代码。需要实现三个方法&#xff1a; Try&#xff1a;资源的检测和预留&#xff1b; Confirm&#xff1a;完成资源操作业务&#xff1b;要求 Try 成…

three.js——几何体划分顶点添加不同的材质

几何体划分顶点添加不同的材质 前言效果图.addGroup(顶点的下标, 获取几个顶点, 选择材质的下标)在vue中使用 前言 上篇文章讲解了怎样通过索引划分顶点&#xff0c;通过顶点绘制图形,本章通过addGroup方法讲解根据划分的顶点来添加不同的材质 效果图 .addGroup(‘顶点的下标’…

精品SpringCloud电影院购票系统-微服务-分布式

《[含文档PPT源码等]精品基于SpringCloud实现的电影院购票系统设计的设计与实现-微服务-分布式》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程等 软件开发环境及开发工具&#xff1a; 开发语言&#xff1a;Java 框架&#xff1a;springcloud JDK版…

【JDK 8-集合框架进阶】6.1 parallelStream 并行流

一、parallelStream 并行流 1.1 串行 和 并行的区别 > 执行结果 二、问题 2.1 paralleStream 并行是否一定比 Stream 串行快? 2.2 是否可以都用并行&#xff1f; > 报错 三、实战 > 执行结果 四、总结 一、parallelStream 并行流 多线程并发处理&#xff…