【C语言】自定义类型

目录

一、结构体:

1、结构体的声明:

2、结构体的自引用:

3、结构体变量的定义和初始化:

4、结构体内存对齐:

5、结构体传参:

6、位段:

二、枚举类型:

三、联合体:


一、结构体:

1、结构体的声明:

首先要了解什么是结构:

结构是一些值的集合,与数组不同的是结构的每一个成员变量可以使不同类型的变量。

其声明的时候用struct关键字加上名字,如下定义一个学生的结构体:

struct Student
{char name[20];int age;char sex[10];char id[20];
};

拓展:也可以声明为匿名结构体(就是没有名字的结构体),这种类型的结构体就没有办法使用了,只有在这种类型后面直接定义变量。

struct 
{char name[20];int age;char sex[10];char id[20];
}s1,s2;

如上,这就定义了一个匿名结构体,其定义了s1,s2这样的全局变量,就可以在函数中使用

2、结构体的自引用:

如果在一个结构体中嵌套一个结构体,那么并不是直接在结构体中直接嵌套一个结构体的,因为这样的话,这个结构体的大小就会计算不了(结构体嵌套一个结构体,嵌套一个结构体里面又会有一个结构体,就会计算不了)那么,就需要在一个结构体中,存放执行向下一个结构体的指针,这样的话大小就可以计算了。

struct Node
{int a;struct Node* next;
};

3、结构体变量的定义和初始化:

1、可以在声明类型的同时定义变量:

struct Student
{char name[20];int age;char sex[10];char id[20];
}s1,s2;

2、可以在全局变量域中定义,并且在定义的同时赋初始值:

struct Node
{int x;int y;
}s1,s2;struct Node s3 = {3,5};

3、若在结构体中引用另一个结构体并初始化:

struct Node1
{int a;int b;
};struct Node2
{char x;struct Node1;float y;
};struct Node2 node = { 'w', { 1,2 }, 3.14f };

4、结构体内存对齐:

结构体对齐规则:
1、第一个成员在与结构体变量偏移量为0的地址处。
2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数:编译器默认的对齐数(VS上是8)和该成员大小的 较小值
        可以用pragma pack(N)来修改默认对齐数,N是几,默认对齐数就修改为几,
pragma pack()这串代码就是取消设置的默认对齐数,还原为默认
3、结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4、如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,
结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

那么为什么会有结构体对齐的规则呢?

在大多数资料都是这么说的:

1、平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能
在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构 ( 尤其是栈 ) 应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存访问仅需要一次访问。

例如:

总的来说:结构体的内存对齐是用空间换时间的做法。

5、结构体传参:

从代码入手:

struct S
{int data[1000];int num;
};struct S s = { {1,2,3,4,5,6,7},13 };void print1(struct S s)
{printf("%d\n", s.num);
}void print2(struct S* s)
{printf("%d\n", s->num);
}
int main()
{print1(s);print2(&s);return 0;
}

在如上代码中,print1就是传结构体即可,print2就是传结构体地址的。

建议:

由于在函数传参的时候,参数是需要压栈的,会有时间和空间上的系统开销,此时如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降,所以在结构体传参的时候,能传结构体的地址就传结构体的地址。

6、位段:

我们首先来看看位段是怎么定义的:

struct PPR
{int a : 2;int b : 6;int c : 13;int d : 31;char e : 7;unsigned int f : 2;
};

如上所示,

注意:

1、位段的成员必须是整型:int, unsigned int ,char等。

2、位段成员后面加上一个:和数字,来表示这个成员占有几个比特位(不能超过变量本身)

部分成员也可以不加冒号和数字。

理解:

位段时根据后面比特位的大小来给空间的,如:

struct PPR
{int a : 2;int b : 6;int c : 13;int d : 31;char x : 2;char e : 7;
};

对于上诉代码的位段理解:

首先第一个成员是int,就开辟32个比特位,接着把a,b,c可以都存放进去(2+6+13<32)

此时d的话放不进去,那么就在开辟32个比特位,将d存放进去。

在进行判断,发现只剩下一个比特位,x存放不进去,就在开辟char类型(8个比特位),然后将x放进去,在判断发现还剩下6个比特位,小于e,就在开辟8个比特位,将e存放进去。

以上就是位段的存放,但是位段有跨平台的问题:

1、int位段被当成有符号数还是无符号数是不确定的

2、位段中最大位的数目不能够确定(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题)

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

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

二、枚举类型:

枚举类型就是将有限的值一 一列举出来,比如:星期,月份等等。

1、定义:用关键字enum来定义,总体和结构体大差不差,

例如星期的定义:(各个枚举常量之间用逗号隔开,最后一个枚举常量后面不需要逗号)

enum Day
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};

2、理解:

这些枚举常量都是有值的,如果均没有赋值就从0开始向下一次增加1;

也可以随便赋值(整型),也是依次向下增加1。

3、优点:

(1)增加代码的可读性和可维护性
(2)和 #define 定义的标识符比较枚举有类型检查,更加严谨。
(3)防止了命名污染(封装)
(4)便于调试

(5)使用方便,一次可以定义多个常量

三、联合体:

联合体也叫共用体,里面的那些成员变量共用同一块空间,

例如:

union Un
{char x;int i;
};

1、特点:

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)
但是当最大成员的大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。
例如:
union u1
{char arr[5];int i;
};union u2
{short arr[7];int i;
};int main()
{printf("%d\n", sizeof(union u1));printf("%d\n", sizeof(union u2));return 0;
}

如上所示:

u1中:char arr[5]这个最大对齐数为1成员大小为5

            int i这个最大对齐数为4成员大小为4,

综合来看:大小应该为5,又要是最大对齐数的整数倍,所以大小就是8.

u2中:short arr[7]这个最大对齐数为2成员大小为14

            int i这个最大对齐数为4成员大小为4,

综合来看:大小应该为14,又要是最大对齐数的整数倍,所以大小就是16./2.

2、应用:

判断计算机的大小端:

int main()
{union Un{int i;char a;}un;un.i = 1;if (un.a == 1)printf("小端\n");elseprintf("大端\n");return 0;
}

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

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

相关文章

网络安全:什么是SQL注入

文章目录 网络安全&#xff1a;什么是SQL注入引言SQL注入简介工作原理示例代码 攻击类型为什么SQL注入危险结语 网络安全&#xff1a;什么是SQL注入 引言 在数字化时代&#xff0c;数据安全成为了企业和个人最关心的问题之一。SQL注入&#xff08;SQL Injection&#xff09;是…

【LLM之RAG】RAT论文阅读笔记

研究背景 近年来&#xff0c;大型语言模型&#xff08;LLMs&#xff09;在各种自然语言推理任务上取得了显著进展&#xff0c;尤其是在结合大规模模型和复杂提示策略&#xff08;如链式思维提示&#xff08;CoT&#xff09;&#xff09;时。然而&#xff0c;LLMs 在推理的事实…

C++的智能指针 RAII

目录 产生原因 RAII思想 C11的智能指针 智能指针的拷贝与赋值 shared_ptr的拷贝构造 shared_ptr的赋值重置 shared_ptr的其它成员函数 weak_ptr 定制删除器 简单实现 产生原因 产生原因&#xff1a;抛异常等原因导致的内存泄漏 int div() {int a, b;cin >> a…

手机usb共享网络电脑没反应的方法

适用于win10电脑&#xff0c;安卓手机上可以 开启usb网络共享选择&#xff0c;如果选择后一直跳&#xff0c;让重复选择usb选项的话&#xff0c;就开启 开发者模式&#xff0c;进到 开发者模式 里设置 默认usb 共享网络 选项 &#xff0c;就不会一直跳让你选。 1.先用数据线 连…

八大经典排序算法

前言 本片博客主要讲解一下八大排序算法的思想和排序的代码 &#x1f493; 个人主页&#xff1a;普通young man-CSDN博客 ⏩ 文章专栏&#xff1a;排序_普通young man的博客-CSDN博客 若有问题 评论区见&#x1f4dd; &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐文章 目录 …

HTTP详细总结

概念 HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则。 特点 基于TCP协议: 面向连接&#xff0c;安全 TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议&#xff0c;在…

Linux管道与重定向

管道 是进程通信的方法之一&#xff0c;在Linux中用命令1|命令2的形式表示&#xff0c;将前一个命令的结果作为后续命令的参数进行输入&#xff0c;也有tee管道&#xff0c;可以进行多次筛选&#xff0c;即多次使用|过滤命令。 重定向 文件描述符FD Linux中输入输出分为三种…

C语言变量、指针的内存关系

1. type p ? 表示从内存地址p开始&#xff0c;开辟一段内存&#xff0c;内存大小为类型type规定的字节数&#xff0c;然后把等号右边的值写入到这段内存中。 因此&#xff0c;这块内存起点位置是p&#xff0c;结束是ptype字节数-1。 2. type* p ?表示从内存地址p开始&…

SpingBoot快速入门下

响应HttpServietResponse 介绍 将ResponseBody 加到Controller方法/类上 作用&#xff1a;将方法返回值直接响应&#xff0c;如果返回值是 实体对象/集合&#xff0c;将会自动转JSON格式响应 RestController Controller ResponseBody; 一般响应 统一响应 在实际开发中一般…

Python学习打卡:day11

day11 笔记来源于&#xff1a;黑马程序员python教程&#xff0c;8天python从入门到精通&#xff0c;学python看这套就够了 目录 day1183、自定义 Python 包创建包导入包方式1方式2方式3方式4 84、安装第三方包安装第三方包——pippip的网络优化 安装第三方包——PyCharm 85、…

代码随想录-Day36

452. 用最少数量的箭引爆气球 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 一支弓箭可以沿着 x 轴从不同点 完全垂…

基于WPF技术的换热站智能监控系统16--动态数据绑定

1、实现思路 1&#xff09;实时读取到的数据绑定到前台UI控件上&#xff0c;这个通过MVVM模式实现&#xff0c;同时注意实时读取必须通过任务task异步方式&#xff0c;这就需要读取PLC数据。 2&#xff09;UI控件的动作&#xff0c;如开或关水泵&#xff0c;必定能够将值写入…

Python | Leetcode Python题解之第169题多数元素

题目&#xff1a; 题解&#xff1a; class Solution:def majorityElement(self, nums: List[int]) -> int:count 0candidate Nonefor num in nums:if count 0:candidate numcount (1 if num candidate else -1)return candidate

Java | Leetcode Java题解之第171题Excel表列序号

题目&#xff1a; 题解&#xff1a; class Solution {public int titleToNumber(String columnTitle) {int number 0;int multiple 1;for (int i columnTitle.length() - 1; i > 0; i--) {int k columnTitle.charAt(i) - A 1;number k * multiple;multiple * 26;}ret…

《Windows API每日一练》5.2 按键消息

上一节中我们得知&#xff0c;Windows系统的按键消息有很多类型&#xff0c;大部分按键消息都是由Windows系统的默认窗口过程处理的&#xff0c;我们自己只需要处理少数几个按键消息。这一节我们将详细讲述Windows系统的所有按键消息及其处理方式。 本节必须掌握的知识点&…

wsl2平台鸿蒙全仓docker编译环境快速创建方法

文章目录 1 文章适用范围&#xff1a;2 WSL环境安装3 镜像迁移非C盘4 Docker环境准备4.1 docker用户组和用户创建4.2 Docker环境配置4.2.1 Ubuntu下安装docker工具4.2.2 鸿蒙Docker环境安装4.2.3 鸿蒙全仓代码拉取编译 5 鸿蒙全仓代码的更新策略6 参考文献7 FAQ7.1 缺头文件xcr…

每天写java到期末考试(6.21)--集合4--练习--6.20

练习1&#xff1a; 正常写集合 bool类 代码&#xff1a; import QM_Fx.Student;import java.util.ArrayList;public class test {public static void main(String[] args) {ArrayList<Student> listnew ArrayList<>();//2.创建学生对象Student s1new Student(&quo…

【面试干货】throw 和 throws 的区别

【面试干货】throw 和 throws 的区别 1、throw1.1 示例 2、throws2.1 示例 3、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java中&#xff0c;throw和throws都与异常处理紧密相关&#xff0c;但它们在使用和含义上有明显的区别。…

【Linux】关于在华为云中开放了端口后仍然无法访问的问题

已在安全组中添加规则: 通过指令: netstat -nltp | head -2 && netstat -nltp | grep 8080 运行结果: 可以看到服务器确实处于监听状态了. 通过指令 telnet 公网ip port 也提示: "正在连接xxx.xx.xx.xxx...无法打开到主机的连接。 在端口 8080: 连接失败"…

[C++][数据结构][B-树][上]详细讲解

目录 0.常见的搜索结构1.B树概念2.B-树的插入分析1.流程分析2.插入过程总结 0.常见的搜索结构 种类数据格式时间复杂度顺序查找无要求 O ( N ) O(N) O(N)二分查找有序 O ( l o g 2 N ) O(log_2 N) O(log2​N)二叉搜索树无要求 O ( N ) O(N) O(N)二叉平衡树无要求 O ( l o g 2 …