[C语言]自定义类型详解:结构体、联合体、枚举

目录

🚀结构体

🔥结构体类型的声明

🔥结构的自引用

🔥结构体变量的定义和初始化

🔥结构体内存对齐

🔥结构体传参

🔥结构体实现位段(位段的填充&可移植性)

🚀枚举

🔥枚举类型的定义

🔥枚举的优点

🔥枚举的使用

🚀联合(共用体)

🔥联合联合类型的定义

🔥联合的特点

🔥联合大小的计算



🚀结构体

🔥结构体类型的声明

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

结构的声明:

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

//struct——>结构体关键字;Stu——>结构体标签这里表示的是学生属性

特殊声明:

//匿名结构体类型
struct
{
    int a;
    char b;
}x;//匿名结构体类型只能使用一次
struct
{
    int a;
    char b;
}a[20],* p;//p指向的是这个结构体指针的地址
int main()
{
    p = &x;
    return 0;
} //编译器会把上面两个声明当成两个完全不同的类型

🔥结构的自引用

在结构体中包含一个类型为该结构本身的成员

struct Node
{
    int data;
    struct Node next;
};

这种类型的结构自引用是非法的,成员next中又会包含一个struct Node的结构,如此递归下去永无止境。在计算sizeof(struct Node)时无法求出。合法声明:

struct Node
{
    int data;
    struct Node* next;
};//也就是说线性结构中链表,每个节点包括了这个节点的数据和指向下一节点的地址的指针的信息。即数据域和指针域。

 当使用typedef类型定义和自引用时要注意下面这种陷阱:

typedef struct
{
    int data;
    Node* next
}Node;//匿名结构体类型,typedef重定义一个名字Node

这种写法是非法的,因为类型名知道整个定义结束才遇到,在结构体内部的Node是未定义的

//解决方案:

typedef struct Node
{
    int data;
    struct Node* next
}Node; 

🔥结构体变量的定义和初始化

struct Point
{
    int x;
    int y;
}s1;         //声明类型的同时定义变量s1
struct Point s2;//定义结构体变量s2

//结构体嵌套初始化

struct Score
{
    int n;
    char ch
};
struct Stu
{
    char name[20];
    int age;
    struct Score s
};
int main()
{
    struct Stu s1 = { "zhangsan",20,{20,'q'} };

🔥结构体内存对齐

如何来计算结构体的大小

#include<stdio.h>
struct S1
{char a;int i;char b;
};
struct S2
{char a;char b;int i;
};
int main()
{printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));return 0;
}

明明只是调换了一下位置,为什么所占字节大小会不同呢?

结构体的对齐规则:

1、第一个成员在与结构体变量偏移量为0的地址处

2、其他成员变量要对齐到某个数字(对其数)的整数倍的地址处。

      对其数=编译器默认的一个对其数与该成员大小的较小值。

           vs中默认的值是8

3、结构体总体大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

4、如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。 

为什么会存在结构体内存?

1、平台原因:

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则会抛出硬件异常。

2、性能原因:

数据结构(尤其是栈)应尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要一次访问。

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

设计结构体的时候,我们既要满足对齐又要满足节省空间,尽量把小的类型集中在前面,从而减少空间的浪费

修改默认对其数

#pragma pack(8)//设置默认对齐数为8
struct S1
{
    char a;
    int i;
    char b;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

#pragma pack(1)//设置默认对齐数为1
struct S2
{
    char a;
    int i;
    char b;  
};

建议不要随意修改,当我们不去追求效率,而是追求空间浪费最少时可以考虑修改默认对齐数。

🔥结构体传参

#include<stdio.h>
struct S
{int data[100];int num;
};
void print1(struct S ss)
{int i = 0;for (i = 0; i < 2; i++){printf("%d ", ss.data[i]);}printf("%d", ss.num);
}
void print2(struct S* ss)
{int i = 0;for (i = 0; i < 2; i++){printf("%d ", ss->data[i]);}printf("%d", ss->num);
}
int main()
{struct S s = { {1,2},100 };print1(s);//传值调用print2(&s);//传址调用return 0;
}

 在进行结构体传参时我们传地址是更好的,这是因为函数传参的时候,参数是需要压栈的,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构以过大,参数压栈的系统开销比较大,会导致性能的下降。

🔥结构体实现位段(位段的填充&可移植性)

位段(位指的是比特位)的声明和结构是类似的,有两个不同:

1、位段的成员只能是整型:int、unsigned int,signed int或者char类型的

2、位段的成员后面有一个冒号和数字

#include<stdio.h>
struct A
{int a : 2;int b : 5;int c : 10;int d : 30;
};

位段的内存分配

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

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

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

//举个栗子:

#include<stdio.h>

struct S
{
    //先开辟一个字节的空间,8个比特位
    char a : 3;
    //用了3个还剩5个比特位
    char b : 4;
    //用了4个还剩1个比特位
    char c : 5;
    //不够用了,再开辟一个字节,8个比特位,用了5个,剩下3个
    char d : 4;
    //又不够用了,再开辟一个字节,8个比特位用了4个,剩下4个
};
int main()
{
    struct S s = { 0 };
    printf("%d\n", sizeof(struct S));//3
    s.a = 10;
    s.b = 12;
    s.c = 3;
    s.d = 4;
    return 0;
}

 

 调试一走,我们发现vs的规则和我们计算的是没有差别的

但我们在使用位段的时候会有很多问题:

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

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

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

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

总结:与结构相比,位段可以达到同样效果,但是可以很好的节省空间,但是有跨平台的问题存在。 当然位段在计算机网络中有其独有的作用,能节省不少空间浪费(数据越少,状态越好),从而达到网络环境较优的状态

🚀枚举

枚举顾名思义就是一一列举,比如:一年12个月可以一一列举、一周7天可以一一列举。

🔥枚举类型的定义

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

enum Sex//性别
{
    MALE,
    FEMALE,
    SECRET
};//与结构体是非常相似的,但其内部是用逗号分隔开的,且内部只包含符号

{ }里面的内容是枚举类型的可能取值,也叫枚举常量。

这些可能取值都是有值的,默认从0开始,一次递增一,当然在定义的时候也可以赋初值。

 enum Color//颜色
{
    RED = 1,
    GREEN=2,
    BLUE=3
};

🔥枚举的优点

我们可以使用 #define 定义常量,为什么要用枚举呢?

1、增加代码的可读性和可维护性

2、和#define定义的标识符比较枚举有类型检查,更加严谨

3、防止命名污染(封装)

4、便于调试

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

🔥枚举的使用

enum Color//颜色
{RED = 1,GREEN=2,BLUE=4
};
int main()
{enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异clr = 5;return 0;
}

🚀联合(共用体)

🔥联合联合类型的定义

联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间(所以联合也叫共用体)

//举例:

union Un
{
    int a;
    char c;
};
int main()
{
    union Un u;
    printf("%d\n", sizeof(u));//4,说明共用了一份空间
    return 0;
}

 从这里我们就能看出来a与c共用一份空间

🔥联合的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合体至少得有能力保存最大的那个成员)

union Un
{int a;char c;
};
int main()
{union Un u;u.a = 0x11223344;u.c = 0x55;printf("%x\n", u.a);return 0;
}

 判断机器是大端存储还是小端存储(用联合的方法)

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

🔥联合大小的计算

·联合的大小至少是最大成员的大小。

·当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。

union Un1
{
    char c[5];
    int i;
};//本来应该是5,最大对齐数为4,所以大小为4的倍数即8

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

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

相关文章

安装 Ubuntu桌面版,详细步骤(附引导 U盘制作工具)

下载镜像 安装Ubuntu首先要下载镜像包&#xff0c;访问下面网址下载镜像包 https://releases.ubuntu.com/ 选择你要安装的Ubuntu版本 将 .iso 文件保存到所需位置&#xff0c;下面会使用此文件创建可引导 U盘。 制作 Ubuntu 引导 U 盘 首先要找到一个大于4G的U盘&#xff…

猫头虎 解析:为什么AIGC在国内适合做TOB,在国外适合做TOC?

猫头虎 解析&#xff1a;为什么AIGC在国内适合做TOB&#xff0c;在国外适合做TOC&#xff1f; 博主 猫头虎 的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面…

ps进程查看命令详解

1、PS 命令是什么 查看它的man手册可以看到&#xff0c;ps命令能够给出当前系统中进程的快照。它能捕获系统在某一事件的进程状态。如果你想不断更新查看的这个状态&#xff0c;可以使用top命令。 2、ps命令支持三种使用的语法格式 UNIX 风格&#xff0c;选项可以组合在一起…

鸿蒙ArkUI-X平台差异化:【运行态差异化(@ohos.deviceInfo)】

平台差异化 简介 跨平台使用场景是一套ArkTS代码运行在多个终端设备上&#xff0c;如Android、iOS、OpenHarmony&#xff08;含基于OpenHarmony发行的商业版&#xff0c;如HarmonyOS Next&#xff09;。当不同平台业务逻辑不同&#xff0c;或使用了不支持跨平台的API&#xf…

Postman快捷功能-批量断言与快速查询替换

大家好&#xff0c;在我们日常的接口测试工作中&#xff0c;经常需要对接口返回的数据进行断言&#xff0c;以确保接口的正确性。当接口数量较多时&#xff0c;逐个编写断言语句会变得非常繁琐。此外&#xff0c;在接口测试过程中&#xff0c;我们还可能需要频繁地查找和替换某…

Python自动化工具(桌面自动化、Web自动化、游戏辅助)

工具介绍 连点工具是一款可以模拟键鼠后台操作的连点器工具。支持鼠标连点、键鼠脚本录制&#xff0c;支持辅助您实现办公自动化以及辅助游戏操作。功能简洁易用&#xff0c;非常方便操作。连点工具让您在在玩游戏、网购抢购的时候全自动点击鼠标&#xff01;主要功能有&#…

MySQL数据库的数据文件保存在哪?MySQL数据存在哪里

在安装好MySQL数据库使用一段时间后&#xff0c;会产生许多的数据库和数据。那这些数据库的数据文件存放在本地文件夹的什么位置呢 一、默认位置 一般来说MySQL数据库的数据文件都是存放在data文件夹之中&#xff0c;但是根据使用的存储引擎不同&#xff0c;产生的一些文件也…

24李林跌落神坛,880还刷吗?还是换1000、900、660?

“李林今年跌落神坛了&#xff01;” “全是固定题型没新题&#xff0c;结果今年考的全是新题。” 880是“老真题的神”&#xff0c; 遇到24年&#xff0c;冷门考点多&#xff0c;计算量又大&#xff0c;就不灵了。 但“老真题”&#xff0c;还是得刷。就像往年真题是要刷的…

(十一)统计学基础练习题五(50道选择题)

本文整理了统计学基础知识相关的练习题&#xff0c;共50道&#xff0c;适用于想巩固统计学基础或备考的同学。来源&#xff1a;如荷学数据科学题库&#xff08;技术专项-统计学二&#xff09;。序号之前的题请看往期文章。 201&#xff09; 202&#xff09; 203&#xff09; 2…

得帆信息PMO总监李健达受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 上海得帆信息技术有限公司aPaaS业务线副总裁、PMO总监李健达先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“AI时代的PMO工作法”。大会将于6月29-30日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#x…

Spring框架学习笔记(五):JdbcTemplate 和 声明式事务

基本介绍&#xff1a;通过 Spring 框架可以配置数据源&#xff0c;从而完成对数据表的操作。JdbcTemplate 是 Spring 提供的访问数据库的技术。将 JDBC 的常用操作封装为模板方法 1 JdbcTemplate 使用前需进行如下配置 1.1 在maven项目的pom文件加入以下依赖 <dependencies…

labelme的使用

创建虚拟环境 听说是要用这个3.6版本的python环境 conda create --namelabelme python3.6激活虚拟环境 activate labelme下载labelme pip install labelme #安装labelme组件启动labelme 在你打开文件的时候推荐还是自己先建立一个label.txt 把自己要分的类别放进去 label.…

翻译《The Old New Thing》- What‘s the deal with the EM_SETHILITE message?

Whats the deal with the EM_SETHILITE message? - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20071025-00/?p24693 Raymond Chen 2007年10月25日 简要 文章讨论了EM_SETHILITE和EM_GETHILITE消息在文档中显示为“未实现”的原因。这些…

前端 JS 经典:Web 性能指标

什么是性能指标&#xff1a;Web Performance Metrics 翻译成 Web 性能指标&#xff0c;一般和时间有关系&#xff0c;在短时间内做更多有意义的事情。 一个站点表现得好与不好&#xff0c;标准在于用户体验&#xff0c;而用户体验好不好&#xff0c;有一套 RAIL 模型来衡量。这…

音视频学习规划

文章目录 概述闲聊点 小结 概述 最近在学习音视频&#xff0c;觉得还是要先写个提纲&#xff0c;给自己制定下学习路线及目标。先写下我的个人流程及思路。 ffmpeg的命令ffmpeg api播放器流媒体RTMP&#xff0c;HLS 闲聊点 先说下学习命令行吧&#xff0c;学习命令行是为了…

Python3 笔记:IDLE的几个基本设置

1、设置字体&#xff1a; Options > Configure IDLE > Fonts 2、设置文字颜色&#xff08;设置高亮&#xff09;&#xff1a; Options > Configure IDLE > Highlights 3、设置背景颜色&#xff1a; Options > Configure IDLE > Highlights 4、设置窗口&a…

各位数字和-第13届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第72讲。 各位数字和&#…

MongoDB(介绍,安装,操作,Springboot整合MonggoDB)

目录 MongoDB 1 MongoDB介绍 MongoDB简介 MongoDB的特点 MongoDB使用场景 小结 2 MongoDB安装 安装MongoDB 连接MongoDB MongoDB逻辑结构 MongoDB数据类型 小结 3 MongoDB操作 操作库和集合 操作文档-增删改 操作文档-查询 MongoDB索引 小结 4 SpringBoot整合…

c# sqlite使用

安装包 使用 const string strconn "Data Sourcedata.db"; using (SQLiteConnection conn new SQLiteConnection(strconn)) {conn.Open();var cmd conn.CreateCommand();cmd.CommandText "select 1";var obj cmd.ExecuteScalar();MessageBox.Show(ob…

ES 查询踩坑-全字段匹配

需求&#xff1a;name字段需要全匹配查询 name的映射 普通的must查询 GET power_engin/_search {"from": 0,"size": 10,"query": {"bool": {"must": [{"term": {"name": {"value": "尼…