C语言——结构体自定义类型

目录

结构体类型

声明结构体

结构体的特殊声明

创建结构体变量和初始化结构体变量

结构体的自引用

结构体内存对齐

对齐规则

内存对齐存在意义

默认对齐数的修改

结构体传参

结构体实现位段

了解位段是什么

位段的内存分配

位段有跨平台的问题及使用注意事项


C语言中有内置的类型,内置类型如下:

内置类型
char

short

int
long
long long
float
double
long double

这些都是C语言本身支持的现场类型,但是仅仅有内置类型是不够的。

比如,我们要定义一个人的变量,

人:3.14   ——   这种就是不行的

人是一个复杂的对象,有身高、体重、名字等。所以这就要用到一个自定义类型——结构体

C语言中也有自定义类型的。

结构体类型

声明结构体

结构体是一些值的集合,这些值成为成员变量,结构的每个成员可以是不同类型的变量。

//结构体的声明
struct tag  //tag就是标签名
{member-list;  //成员列表:1个或者多个,但是不能没有
}variable_list;  //变量列表

我们知道结构体的声明之后,我们就可以自定义一个学生练习一下:

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

结构体的特殊声明

在声明结构体的时候,可以不完全声明:匿名结构体类型

//匿名结构体类型
struct
{int a;char b;float c;
}x;struct
{int a;char b;float c;
}a[20], * p;

这两个结构体对比前面的,看出tag省略了

在使用这种形式的时候,要注意两点:

  1. 匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次,第二次往后基本不能用了。
  2. 编译器会把上面的两个声明当成完全不同的两个类型,所以下面操纵是非法的。
p = &x;

创建结构体变量和初始化结构体变量

我们定义了这个类型,要怎么创建这种类型的变量并怎么初始化它呢?

局部结构体变量创建和初始化有两种:

一种按照结构体成员的顺序初始化的,如下:

//按照结构体成员的顺序初始化
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
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
};             *p=Stu;
Stu.name==(*p).name==p->name。

第二种初始化按照指定顺序初始化的(乱序):

//按照指定的顺序初始化
struct Stu s2 = { .age = 18, .name = "李四", .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);

                                

全局变量的定义有两种,初始化方式也跟局部变量相似,下面就只举例全局变量的创建:

struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}b2,b3; //全局变量           struct Stu b1;//全局变量

结构体的自引用

先介绍typedef相关知识, typedef为C语言的关键字:作用是为一种数据类型定义一个新名字

typedef可以声明新的类型名来代替已有的类型名,不能增加新的类型。这里的数据类型有前面所说的内置数据类型(int,float等),还有自定义的数据类型(struct等)。

结构体自引用对于数据结构上的链表是非常有用的,

数据结构——其实是数据在内存种的存储和组织的结构,数据结构有多种

  • 线性数据结构:顺序表、链表、栈、队列。
  • 树形数据结构:二叉树
  • 等等……

结构体自引用的正确方式(链表形式):

struct Node
{int data;			//数据struct Node* next;  //指针——自己里包含一个自己同类型的指针
};

我们用typedef重命名一下:

typedef struct Node
{int data;			//数据struct Node* next;  //指针——自己里包含一个自己同类型的指针
}Node;    //将struct Node 类型重命名为 Node
//也可也写成
struct Node
{int data;			//数据struct Node* next;  //指针——自己里包含一个自己同类型的指针
};
typedef struct Node Node;  //typedef在后面重命名

注意,千万不能写成以下这些形式:

struct Node
{int data;struct Node next;
};
一个结构体中再包含一个同类型的结构体变量,这样结构体变量的大小就会无穷的大。
typedef struct   //匿名结构体类型
{int data;Node* next;  //这里面还有struct不能省略掉
}Node;

这个Node是对前面的匿名结构体类型的重命名产生的,但在匿名结构体内部提前使用Node类型来创建变量成员变量是不可以的。所以匿名结构体类型是不能实现这种自引用的。

所以,定义结构体尽量不要使用匿名结构体。

结构体内存对齐

这个牵扯到计算结构体的大小,并且还有它特有的对齐规则。

对齐规则

  1. 结构体的第一个成员对齐到结构体变量起始位置偏移量为0的地址处。(后面举例介绍)
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。                                             对齐数=编译器默认的一个对齐数与该成员变量大小的较小值                                                   VS中默认的一个对齐数为8                                                                                                       Linux中gcc没有默认对齐数,对齐数就是成员变量自身大小。
  3. 结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大的)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

例如下列例子,并了解这个规则怎么用:

struct S1
{char c1;int i;char c2;
};
printf("%d\n", sizeof(struct S1));struct S2
{double d;char c;int i;
};
printf("%d\n", sizeof(struct S2));
//结构体嵌套问题
struct S3
{char c1;struct S2 s2;double d;
};
printf("%d\n", sizeof(struct S3));

第一个:

所以会输出:12.

注:若遇到数组,如char[5] 则就是存了五个char。

第二个:

所以这个输出16.

第三个:

所有会输出:32.

内存对齐存在意义

结构体内存对齐是牺牲空间来换取时间的做法。

1. 平台原因 (移植原因)
2. 性能原因
若又想节省一些空间,那么就让空间小的成员尽量集中在一起。
struct S1
{char c1;int i;char c2;
};  //占12struct S2
{char c1;char c2;int i;
};  //占8

默认对齐数的修改

我们前面说VS中默认对齐数是8,那么我们可以修改吗?

我们可以用一个预处理指令修改,并且另一个指令可以取消修改,如下:

#pragma pack(1)//设置默认对⻬数为1
#pragma pack()//取消设置的对⻬数,还原为默认

根据自己所需,可以用这个预处理指令修改对齐数。

结构体传参

我们函数传参,可以传整型变量、数组、指针变量等。结构体变量也可以传。

实现具体如下列程序:

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;
}

不过上面两个函数,print2的效率高一些。

所以,结构体传参的时候,尽量传结构体的地址。

结构体实现位段

了解位段是什么

位段实现是基于结构体的

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

  1. 位段的成员必须是int、unsigned int 或者signed int,C99中位段成员的类型可以选择其他类型。
  2. 位段成员名后边有一个冒号和一个数字。
    struct A
    {int _a:2;
    };

位段的内存分配

  1. 位段的成员可以是int、unsigned int、signed int 或者char类型等
  2. 位段的空间上是按照需要以4个字节(int)或1个字节(char)的方式来开辟的
  3. 位段很多不确定因素,因此不能跨平台的,可移植程序应避免使用位段。

我们看下一段代码:

struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};int main()
{printf("%d\n", sizeof(struct A));return 0;
}

A所占内存是多少?

输出结果:

位段有跨平台的问题及使用注意事项

int位段被当成有符号还是无符号不确定

位段最大位的数目不确定。

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

当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃 剩余的位还是利用是不确定的。

所以位段可以达到结构体同样的效果,并且可以很好的节省空间,但有跨平台问题。

注意事项:位段内存中每个节分配一个地址,一个字节内部的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;
}

制作不易,求各位大佬三连qwq,若有不足的地方,请大佬们多多指点!

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

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

相关文章

Spark-Scala语言实战(3)

在之前的文章中,我们学习了如何在来如何在IDEA离线和在线安装Scala,想了解的朋友可以查看这篇文章。同时,希望我的文章能帮助到你,如果觉得我的文章写的不错,请留下你宝贵的点赞,谢谢。 Spark-Scala语言实…

[Halcon学习笔记]标定常用的Halcon标定板规格及说明

1、介绍 大多数标定的要求都是以实心圆或方格来作为标志点,所以一般的标定板为棋盘格或矩阵圆点图,高精度的相机标定过程中,大多是以比较明确的特征点来作为参考,所以通过识别标定板的圆形,拟合出精确的中心位置&…

3.19总结

A计划 题解&#xff1a;这题其实就是一个很简单的三维搜索&#xff0c;有了一个传送门&#xff0c;并且要确定是否传过去的对应位置是墙&#xff0c;防止被装死&#xff0c;同事呢又要在对应的t时间内完成&#xff08;不一定要卡着t时间恰好完成&#xff09; #include<ios…

linux单机部署hadoop

1.下载安装包 https://archive.apache.org/dist/hadoop/common/ 2.上传压缩 3.修改配置文件 1)设置JDK的路径 cd /usr/local/software/hadoop-3.1.3/etc/hadoop vi hadoop-env.sh export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.402.b06-1.el7_9.x86_64/ 查看…

1Panel应用推荐:Nginx Proxy Manager

1Panel&#xff08;github.com/1Panel-dev/1Panel&#xff09;是一款现代化、开源的Linux服务器运维管理面板&#xff0c;它致力于通过开源的方式&#xff0c;帮助用户简化建站与运维管理流程。为了方便广大用户快捷安装部署相关软件应用&#xff0c;1Panel特别开通应用商店&am…

springBoot---过滤器,监听器,拦截器

过滤器&#xff0c;监听器&#xff0c;拦截器 一、理解它们 看里十几篇博客&#xff0c;总算有点小明白&#xff0c;总的来讲&#xff0c;两张图可以让我看明白点。 通过两幅图我们可以理解拦截器和过滤器的特点 1、过滤器 过滤器是在请求进入tomcat容器后&#xff0c;但请求…

2024流星全自动网页生成系统重构版源码

2024流星全自动网页生成系统重构版源码 源码介绍 流星全自动网页生成系统重构版源码分享&#xff0c;所有模板经过精心审核与修改&#xff0c;完美兼容小屏手机大屏手机&#xff0c;以及各种平板端、电脑端和360浏览器、谷歌浏览器、火狐浏览器等等各大浏览器显示。 为用户使…

【第十五章】改进神经网络学习方式-手写数字识别重新编码实现

让我们来实现我们在之前讨论过的想法。我们将开发一个新的程序&#xff0c;network2.py&#xff0c;这是我们之前开发的程序 network.py 的改进版本。如果你有一段时间没有看过 network.py&#xff0c;那么花几分钟快速阅读之前的讨论可能会有所帮助。它只有 74 行代码&#xf…

44.for语句

目录 一.什么是for语句 二.语法格式 三.举例 四.视频教程 一.什么是for语句 C语言中除了while和do while循环语句&#xff0c;还有for循环语句&#xff0c;所以for语句也是循环语句。 二.语法格式 for&#xff08;表达式1&#xff1b;表达式2&#xff1b;表达式3&#xf…

8.python中的元组

8.python中的元组 虽然说元组用的不是很多&#xff0c;但是还是讲一下。元组其实可以看为是一个列表&#xff0c;不可变的列表。操作和列表都差不多。 元组&#xff08;tuple&#xff09;是Python中的一种基本数据结构类型&#xff0c;它是不可变的序列&#xff0c;意味着一旦…

【爬虫】实战-爬取Boss直聘信息数据

专栏文章索引&#xff1a;爬虫 所用工具&#xff1a; 自动化工具&#xff1a;DrissionPage 目录 一、找到目标数据(2个确定)​ 1.确定目标网页 2.确定目标网址 二、编写代码​ 三、查看数据​ 五、总结 一、找到目标数据(2个确定) 1.确定目标网页 打开目标网站 网站&am…

DolphinScheduler运维-页面加载缓慢

一、问题描述 DolphinScheduler调度平台的UI界面加载缓慢,项目中的任务实例加载时间过长,需要解决这个问题,提高DolphinScheduler平台UI页面的加载速度。 二、原因分析 经过分析发现,任务实例过多是导致UI加载缓慢的主要原因。由于任务实例无法直接删除,根据文档了解到需…

狼人杀 魔镜少女 个人玩法理解

最近出来的新板子 (魔镜少女 觉醒隐狼) 确实是非常的好玩 先来说说板子身份 好人阵营: 神职牌有 魔镜少女 女巫 猎人 守卫 民牌有: 四个普通平民 狼人阵营: 觉醒隐狼 四个普通小狼人 先来说说技能 魔镜少女每晚可以翻拍查验一名玩家的具体身份&#xff0c;注意 是具体身份 就…

网络架构层_交换机连接使用

网络架构层_交换机连接使用 交换机是不是不会用呀&#xff1f;交换机&#xff0c;服务器&#xff0c;路由器&#xff0c;防火墙&#xff0c;网关&#xff0c;这些都是嘛呀&#xff1f; 网上的一些网络架构图&#xff0c;具体项目按照实际考虑。 交换机的Console口——通过Con…

c语言指针(二)

c语言指针&#xff08;二&#xff09; 1.数组名的理解 2.使用指针访问数组 3.一维数组的传参本质 1.数组名的理解 int arr[10] { 1,2,3,4,5,6,7,8,9,10 }; int* p &arr[0]这⾥我们使⽤ &arr[0] 的⽅式拿到了数组第⼀个元素的地址&#xff0c;但是其实数组名本来就是…

【链表】Leetcode 142. 环形链表 II【中等】

环形链表 II 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系…

Linux用户、用户组

用户管理命令&#xff1a; 首先要先知道两个配置文件&#xff1a;/etc/group 用户组配置文件/etc/passwd 保存了所有用户的用于读取的必要信息**/etc/shadow **是 Linux 系统中用于存储用户密码信息的文件。这个文件也被称为“影子文件”&#xff0c;因为它包含了 /etc/passwd…

网站如何搭建 网站搭建的详细步骤

网站如何搭建 网站搭建的详细步骤 一.领取一个免费域名和SSL证书&#xff0c;和CDN 1.打开网站链接&#xff1a;https://www.rainyun.com/z22_ 2.在网站主页上&#xff0c;您会看到一个"登陆/注册"的选项。 3.点击"登陆/注册"&#xff0c;然后选择"…

打破沟通壁垒:跨部门需求冲击与IT部门的应对智慧

引言 在快节奏、高要求的互联网行业&#xff0c;跨部门间的有效沟通是确保项目顺利进行和公司业务稳定发展的基石。然而&#xff0c;需求突袭往往成为打乱这一稳定局面的重要因素。 事件的背景 作为一IT部门负责人&#xff0c;在跨部门的领导层沟通会议上&#xff0c;一个在事…

2024蓝桥杯每日一题(回溯)

备战2024年蓝桥杯 -- 每日一题 Python大学A组 试题一&#xff1a;木棒 试题二&#xff1a;n皇后问题 试题三&#xff1a;糖果 试题四&#xff1a;飞机降落 试题五&#xff1a;生日蛋糕 试题一&#xff1a;木棒 【问题描述】 乔治拿来一组等长…