【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.mzph.cn/bicheng/31166.shtml

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

相关文章

【设计模式深度剖析】【10】【行为型】【状态模式】

&#x1f448;️上一篇:访问者模式 | 下一篇:解释器模式&#x1f449;️ 设计模式-专栏&#x1f448;️ 文章目录 状态模式定义英文定义直译如何理解呢&#xff1f; 状态模式的角色Context&#xff08;环境类&#xff09;State&#xff08;抽象状态类&#xff09;Concret…

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

题目&#xff1a; 题解&#xff1a; class Solution {public int majorityElement(int[] nums) {int count 0;Integer candidate null;for (int num : nums) {if (count 0) {candidate num;}count (num candidate) ? 1 : -1;}return candidate;} }

Netdata介绍

前言 Netdata是一款用于Linux系统的实时性能监测工具&#xff0c;它提供了web界面的视角&#xff0c;使得用户可以通过可视化的方式清晰地了解系统和应用程序的实时状态。 Netdata具有以下几个显著特点&#xff1a; 实时性&#xff1a;Netdata能够实时监测系统和应用程序的性…

Android-Android Studio-FAQ

1 需求 2 接口 3 Android Studio xml布局代码补全功能失效问题 最终解决方案就是尝试修改compileSdk 为不同SDK版本来解决问题&#xff0c;将原本34修改为32测试会发现xml代码补全功能有效了&#xff01; 参考资料 Android Studio xml布局代码补全功能失效问题_android studi…

电压调整+无功优化!考虑泄流效应的风电场并网点电压系统侧增援调控方法程序代码!

前言 在发电侧能源结构转型的背景下&#xff0c;中国在可再生能源发电技术上的快速发展使得电网中风电并网比例不断增大。风能资源的有效利用缓解了电力紧张&#xff0c;但由于风速的不确定性&#xff0c;风电场引起的公共耦合点&#xff08;point of common coupling&#xf…

vue3项目使用Electron打包成exe的方法与打包报错解决

将vue3项目打包成exe文件方法 一、安装 1.安装electron npm install electron --save-devnpm install electron-builder --save-dev 2.在vue项目根目录新建文件index.js // index.js// Modules to control application life and create native browser window const { app…

Map-JAVA面试常问

1.HashMap底层实现 底层实现在jdk1.7和jdk1.8是不一样的 jdk1.7采用数组加链表的方式实现 jdk1.8采用数组加链表或者红黑树实现 HashMap中每个元素称之为一个哈希桶(bucket),哈希桶包含的内容有以下4项 hash值&#xff08;哈希函数计算出来的值&#xff09; Key value next(…

基于SSM+Jsp的水果销售管理网站

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

Linux 五种IO模型

注&#xff1a;还有一种信号驱动IO&#xff0c;使用较少暂不讨论&#xff1b; 一&#xff0c;区分阻塞、非阻塞和同步、异步 看了很多文章对这两组概念解释和对比&#xff0c;说的太复杂了&#xff0c;其实没必要&#xff0c;两句话就能说清楚。 首先&#xff0c;对于读数据rec…

探索监管沙箱在金融科技行业中的应用

一、引言 随着金融科技的快速发展&#xff0c;传统金融机构与科技企业之间的竞争也日趋激烈。为了平衡金融科技创新与风险防控&#xff0c;各国监管机构纷纷引入监管沙箱&#xff08;Regulatory Sandbox&#xff09;机制。监管沙箱作为一个受监督的安全测试区&#xff0c;允许金…

Linux字节对齐小程序

#include <stdio.h> // 默认对齐 struct DefaultAligned { char c; int i; }; // 按1字节对齐 #pragma pack(push, 1) struct OneByteAligned { char c; int i; }; #pragma pack(pop) // 恢复之前的对齐设置 int mai…

Python日志管理利器:如何高效管理平台日志

一、为什么需要日志管理&#xff1f; 日志是应用程序的重要组成部分&#xff0c;它记录了应用程序的运行状态、错误信息以及用户交互等关键信息。良好的日志管理可以帮助开发人员及时发现和解决问题&#xff0c;提高应用程序的稳定性和可靠性。 项目在本地开发调试时&#xf…

基于Sringboot+Vue的校园招聘系统【原创】【开源】

浏览器&#xff1a;Chrome或360浏览器 系统环境配置 前置条件&#xff1a;系统已经安装了Mysql5.7、Mysql工具&#xff08;Navicat&#xff09;、JDK1.8、Maven3.6.1、vue3.0以下开发环境、 Intellij Idea、 Chrome或360浏览器 1、导入数据库 2、编译前端代码vue 编译&…

HTML播放flv

页面效果&#xff1a; 代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" …

森林火灾扑救特类车辆有哪些_鼎跃安全

森林消防是在森林火灾发生时&#xff0c;为了保护森林资源&#xff0c;防止火势蔓延&#xff0c;采取了一系列的应用措施&#xff0c;针对自然环境中的火灾消防工作。森林灭火主要包括预警、预防措施、火情监测、火势控制和灭火等&#xff0c;森林火灾发生的地形往往复杂崎岖&a…

【银河麒麟】高可用触发服务器异常重启,处理机制详解

1.服务器环境以及配置 【机型】物理机 处理器&#xff1a; Intel 内存&#xff1a; 126G 【内核版本】 4.19.90-25.16.v2101.ky10.x86_64 【银河麒麟操作系统镜像版本】 Kylin-Server-10-SP2-Release-Shenzhen-Metro-x86-Build01-20220619 Kylin-HA-10-SP2-Release-S…

yolov10打包为exe

一、前言 本节实验将官方yolov10推理程序打包为exe运行 二、代码 首先下载官方代码至本机&#xff0c;并使用conda创建虚拟环境&#xff0c;并安装好yolov10所需库 conda create --prefix E:/pyenv/myYolo10 python3.8 pip install -r requirements.txt 下载官方模型权重 …

深入理解和实现Windows进程间通信(消息队列)

常见的进程间通信方法 常见的进程间通信方法有&#xff1a; 管道&#xff08;Pipe&#xff09;消息队列共享内存信号量套接字 下面&#xff0c;我们将详细介绍消息队列的原理以及具体实现。 什么是消息队列&#xff1f; Windows操作系统使用消息机制来促进应用程序与操作系…

Git 查看当前分支是基于哪个分支拉取(源头分支)

场景&#xff1a; 项目中使用 Git 管理代码仓库的时候&#xff0c;随着项目的持续迭代及项目的扩展&#xff0c;多版本并行开发是非常常见的事情&#xff0c;多版本并行开发就伴随着多分支&#xff0c;随着 Git 的分支越拉越多&#xff0c;这时候很容易造成分支的混乱&#xf…

蓝牙模块在车载系统中的应用与集成:现状、挑战与未来展望

随着科技的快速发展&#xff0c;蓝牙技术已经深入到我们生活的方方面面&#xff0c;其中车载系统中的应用尤为显著。蓝牙模块作为一种无线通信技术&#xff0c;不仅为驾驶者提供了更加便捷的操作体验&#xff0c;同时也提升了驾驶的安全性。本文旨在分析蓝牙模块在车载系统中的…