[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.rhkb.cn/news/331923.html

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

相关文章

FFmpeg操作命令 - 精简版

PS&#xff1a;&#xff08;因为我只需要简单的操作&#xff0c;所以我整理出了这份笔记&#xff09; 原网址&#xff1a;30分钟带你入门&#xff0c;20个 FFmpeg操作命令&#xff0c;包你学会 - 知乎 大佬零声Github整理库整理的笔记非常的全面&#xff0c;想看完整版去上面…

LeetCode 第131场双周赛个人题解

100309. 求出出现两次数字的 XOR 值 原题链接 求出出现两次数字的 XOR 值 - 力扣 (LeetCode) 竞赛 思路分析 签到题&#xff0c;一次遍历 AC代码 class Solution:def duplicateNumbersXOR(self, nums: List[int]) -> int:cnt Counter(nums)res 0st set(nums)for x …

Leetcode42题:接雨水

1.题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,…

【Redis】 关于 Redis 哈希类型

文章目录 &#x1f343;前言&#x1f38b;命令介绍&#x1f6a9;hset&#x1f6a9;hget&#x1f6a9;hexists&#x1f6a9;hdel&#x1f6a9;hkeys&#x1f6a9;hvals&#x1f6a9;hgetall&#x1f6a9;hmget&#x1f6a9;hlen&#x1f6a9;hsetnx&#x1f6a9;hincrby&#x1…

SAP-CO成本控制概念之标准成本

“ 本篇介绍&#xff1a;标准成本的会计概念&#xff0c;标准成本的制定标准&#xff1b;通过结合会计标准成本的概念与SAP CO标准成本估算功能&#xff0c;更具象化的了解SAP如何实现标准成本管理&#xff0c;为后续学习SAP实际成本核算打下基础。” 01 — 背景需求 SAP实施…

最重要的时间表示,柯桥外贸俄语小班课

в第四格 1、与表示“钟点”的数词词组连用 例&#xff1a; в шесть часов утра 在早上六点 в пять тридцать 在五点半 2、与表示“星期”的名词连用 例&#xff1a; в пятницу 在周五 в следующий понедельник …

LeetCode 第399场周赛个人题解

100323. 优质数对的总数 I 原题链接 100323. 优质数对的总数 I 思路分析 签到题 AC代码 class Solution:def numberOfPairs(self, nums1: List[int], nums2: List[int], k: int) -> int:n, m len(nums1), len(nums2)ret 0for i in range(n):for j in range(m):if nu…

千亿级开源大模型Qwen110B部署实测

近日&#xff0c;通义千问团队震撼开源 Qwen1.5 系列首个千亿参数模型 Qwen1.5-110B-Chat。 千亿级大模型普通显卡是跑不了推理的&#xff0c;普通人一般也没办法本地运行千亿级大模型。 为了探索千亿级大模型到底需要计算资源&#xff0c;我用云计算资源部署了Qwen1.5-110B-…

Leetcode 剑指 Offer II 079.子集

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返…

20232801 2023-2024-2 《网络攻防实践》实践十一报告

#20232801 2023-2024-2 《网络攻防实践》实践十一报告 1.实践内容 &#xff08;1&#xff09;web浏览器渗透攻击 使用攻击机和Windows靶机进行浏览器渗透攻击实验&#xff0c;体验网页木马构造及实施浏览器攻击的实际过程。 &#xff08;2&#xff09;取证分析实践—网页木马…

北核论文完美复现:自适应t分布与动态边界策略改进的算术优化算法

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 原始算术优化算法 改进点1&#xff1a;引入…

那智不二越机器人维修案例分享

那智不二越工业机器人在工业范围内广泛应用于各种生产领域。其示教器作为人机交互的重要设备&#xff0c;常常需要定期维护和Nachi不二越机械手示教盒修理。 【Nachi不二越机器人示教器维修步骤】 1. 关闭电源 在进行任何那智不二越机器人维修操作之前&#xff0c;务必确保机器…

IDEA打开项目报错

IDEA打开项目报错&#xff1a; Cannot read scheme C:\Users\xxxxxx\AppData\Roaming\JetBrains\IntelliJIdea2023.2\qaplug_profiles\Default.xmljava.lang.AbstractMethodError: Receiver class com.soldevelo.qaplug.scanner.AnalysisProfileManager$2 does not define or i…

单条16g和双条8g哪个好

单条16g和双条8g各有优劣,具体选择要根据个人需求和电脑配置来决定。 以下是一些参考信息: •单条16g内存的价格比双条8g内存的价格低,而且16g的内存容量大,一条内存十分的方便。 •两条8g内存可以组成双通道,电脑运行速度要快一些。 •对于普通使用电脑的人群与热衷于…

项目管理-人力资源管理

目录 一、概述 二、人力资源计划编制 2.1 概述 2.2 层次结构图 2.3 分配任务矩阵 三、组建项目团队 3.1 概述 3.2 内部谈判 3.3 事先分派 3.4 外部招聘 3.5 虚拟团队 3.6 总结 四、项目团队建设 4.1 概述 4.2 团队发展过程 4.2.1 概述 4.2.2 形成期 4.2.3 震…

vue3 ts问题 找不到模块“@/views/home/index.vue”或其相应的类型声明。

1. 找不到模块“/views/HomeView.vue”或其相应的类型声明 今天帮同事看了一个问题&#xff0c;他尝试用vitevue3tspinia创建项目&#xff0c;结果刚上来就遇到这么一个问题 2. 解决办法 出现这个问题的原因就是&#xff1a;ts只支持导出导入模块&#xff0c;但是vue不是模块…

红蓝对抗-HW红蓝队基本知识(网络安全学习路线笔记)

第一, 什么是蓝队 蓝队&#xff0c;一般是指网络实战攻防演习中的攻击一方。 蓝队一般会采用针对目标单位的从业人员&#xff0c;以及目标系统所在网络内的软件、硬件设备同时执行多角度、全方位、对抗性的混合式模拟攻击手段&#xff1b;通过技术手段实现系统提权、控制业务、…

利用Python去除PDF水印

摘要 本文介绍了如何使用 Python 中的 PyMuPDF 和 OpenCV 库来从 PDF 文件中移除水印&#xff0c;并将每个页面保存为图像文件的方法。我们将深入探讨代码背后的工作原理&#xff0c;并提供一个简单的使用示例。 导言 简介&#xff1a;水印在许多 PDF 文件中都很常见&#x…

自建公式,VBA在Excel中轻松获取反义词

自建公式&#xff0c;VBA在Excel中轻松获取反义词 文章目录 前言一、爬取网站数据二、代码1.创建数据发送及返回方法2.汉字转UTF8编码2.获取反义词 三、运行效果截图 前言 小学语文中&#xff0c;近义词、反义词是必考内容之一。家长不能随时辅导怎么办&#xff1f;有VBA&…