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

自定义类型详解

  • 前言:
  • 一、结构体
    • 1.1结构体的声明
    • 1.2结构体内存对齐
    • 1.3位段(位域)
  • 二、枚举
    • 2.1枚举类型的定义
    • 2.2枚举类型的优点
    • 2.3枚举的使用
  • 三、联合体
    • 3.1联合体类型的定义
    • 3.2联合体的特点
    • 3.3联合体大小的计算

前言:

我打算把结构体、枚举、联合体的重点内容总结一下,方便后期复习的时候能够更快,更准确的去拾取遗忘的知识。也希望能给大家起到借鉴的作用,不足的地方,请多多包涵。(不足的地方,也希望大家能够指出来)

一、结构体

1.1结构体的声明

结构体是一些值的集合,这些集合称为成员变量,结构体的每个成员可以是不同类型的变量。
结构体的声明:
在这里插入图片描述
结构体特殊声明:匿名结构体
如:

struct
{
int a;
char b;
float c;
}x;

结构体的自引用:

typedef struct
{
int data;
struct node* next;
}Node;
这样写代码不行,匿名结构体不要自引用
比较好的自引用方式:
typedef struct node
{
int data;
struct node* next;
}Node;
先用结构体类型,Node命名在后面

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

struct point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct point p2;//定义结构体变量p2
//初始化:定义变量的同时赋初值
struct point p3={x,y};
struct Stu //类型声明
{
char name[15];//名字
int age;//年龄
};
struct Stu s={“pan long”,22};//初始化
结构体嵌套初始化:
struct Node
{
int data;
struct point;
struct Node* next;
}n1={22,{4,5},NULL};
struct Node n2={22,{6,7},NULL};//结构体嵌套初始化

1.2结构体内存对齐

结构体大小:

#include <stdio.h>
struct s1
{char c1;//1int i;//4char c2;//1
};
struct s2
{double d;//8char c;//1int i;//4
};
//结构体嵌套问题
struct s3
{char a;//1struct s2 S2;//16double d;//8
};
int main()
{printf("%d\n", sizeof(struct s1));//打印12printf("%d\n", sizeof(struct s2));//打印16printf("%d\n", sizeof(struct s3));//打印32return 0;
}

上面的现象分析:我们发现结构体成员不是按照顺序在内存中连续存放的,有一定的对齐规则
结构体内存对齐的规则:
1.结构体的第一个成员永远放在相较于结构体变量起始位置的偏移量为0的位置。
2.从第二个成员开始,往后的每个成员都要对齐到某个对齐数的整数倍处。
对齐数:结构体成员自身的大小和默认对齐数的较小值。
VS上默认对齐数是8
gcc 没有默认对齐数,对齐数是结构体成员的自身大小
3.结构体的总大小,必须是最大对齐数的整数倍。
最大对齐数是:所以成员的对齐数中最大的值。
为什么存在内存对齐?

1.平台原因:不是所以的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定的类型的数据,否则就会硬件异常。
2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因是:为了访问未对齐的内容,处理器需要做两次内存访问;而对齐的内存访问仅需一次访问。
总结:结构体的内存对齐是拿空间来换取时间的做法。

怎么修改默认对齐数?

#pragma是预处理命令,我们使用它可以改变默认对齐数。
如:#pragma pack(8)//设置默认对齐数为8
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
结论:结构体在对齐方式不合适的时候,我们可以自己改默认对齐数。

结构体传参:
有两种方式:

//定义一个结构体
struct S
{int data[100];int num;
};
struct S s = { {1,2,3,4},1000 };
void printf1(struct S s)
{printf("%d\n", s.num);
}
void printf2(  struct S* ps)
{printf("%d\n", ps->num);
}
int main()
{printf1(s);//值传参printf2(&s);//地址传参return 0;
}

地址传参更好
原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。如果传一个结构体对象,结构体够大,参数压栈的时候开销比较大,所以会导致性能的下降。
结论:结构体传参的时候,要传结构体的地址。

1.3位段(位域)

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

  1. 位段的成员必须是int 、unsigned int、或者 signed int。
  2. 位段的成员名后边有一个冒号和一个数字
    如:
    A就是一个位段类型
#include <stdio.h>
struct A
{
//占的是二进制位int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
int main()
{printf("%d\n",sizeof(struct A));//打印结果为8
}

位段的内存分配:
位段的成员可以是int 、unsigned int、signed int或者是char(属于整型家族)的类型。
位段在空间上是按照需要以4个字节和1个字节方式一次性去开辟得。
位段涉及很多不确定得因素,位段是不跨平台的,
例子:

#include <stdio.h>
struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};
int main()
{struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;return 0;
}

空间是如何开辟得?
在这里插入图片描述
位段跨平台问题:

1、int 位段被当成有符号数还是无符号数是不确定的。
2、位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题)
3、位段中的成员在内存中从左向右分配,还是从右向左分配标志尚未定义。
4、当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不明确的。
结论:
跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

二、枚举

2.1枚举类型的定义

枚举就是列举的意思,把可能的取值一一列举。
如:一个星期有7天,它是有限得,那么我们就可以一一列举。
一年有12个月,它是有限得,那么我们可以一一列举。

enum Day //星期
{
Mon,Tues,Wed,Thur,Fri,Sat,Sun
};
enum Sex //性别
{
Male,Female,Secret
};
以上都是枚举类型
{}中的内容是枚举类型的可能取值,也叫做枚举常量.
这些可能取值都是有值的,默认从0开始,依次递增1,在声明枚举类型的时候也可以赋初值。

2.2枚举类型的优点

枚举类型的优点:

  1. 增加代码的可读性和可维护性。
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
  3. 便于调试。
  4. 使用方便,一次可以定义多个常量。

2.3枚举的使用

#include <stdio.h>
enum color
{red = 1,green = 2,blue = 4
};
int main()
{enum color  s= red;// s = 3;//在c++里无法从int型转换为枚举类型printf("%d\n", s);return 0;
}

三、联合体

3.1联合体类型的定义

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

#include <stdio.h>
//联合体类型声明
union un
{char c;int i;
};
int main()
{union un s;//联合体变量的定义printf("%d\n", sizeof( s));//计算共用体变量的大小,打印结果为5return 0;
}

3.2联合体的特点

联合体的成员是共用同一块内存空间,联合体变量的大小,至少是最大成员的大小(联合体至少能保存最大的那个成员)
例子:

#include <stdio.h>
union S
{char a;int i;
}s = {0};
int main()
{s.i = 1;printf("%d\n", s.a);//打印1,说明是小端s.i = 0x11223344;//小端存储,低字节内容存到低地址中s.a = 0x55;printf("%x\n", s.i);//打印0x11223355return 0;
}

3.3联合体大小的计算

联合的大小至少是最大成员的大小
当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。
#include <stdio.h>
union S
{
char a[5];//5
int i;//4
} s;
int main()
{
//共用体占多少内存空间
printf(“%d\n”, sizeof(s));//打印结果为8
return 0;
}

1、最大成员是5个字节。
2、最大成员不是最大对齐数的整数倍4(char为1,int为4),让他变成整数倍,所以打印结果为8.

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

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

相关文章

Android 13 Hotseat定制化修改

一.背景 由于需求是需要自定义修改Hotseat,所以此篇文章是记录如何自定义修改hotseat的,应该可以覆盖大部分场景,修改点有修改hotseat布局方向,hotseat图标数量,hotseat图标大小,hotseat布局位置,hotseat图标禁止形成文件夹,hotseat图标禁止移动到Launcher中,下面开始…

实例036 使窗体标题栏文字右对齐

实例说明 窗口标题栏中的文字是窗口的重要说明&#xff0c;该文字可以标示窗口的功能、状态或名称等信息&#xff0c;一般该文字是居左显示的&#xff0c;在本例中设计一个标题栏文字右对齐的窗口。本实例运行结果如图1.36所示。 技术要点 在C# 2.0中实现这一功能非常容易&am…

【LeetCode】105. 从前序与中序遍历序列构造二叉树

作者&#xff1a;小卢 专栏&#xff1a;《Leetcode》 喜欢的话&#xff1a;世间因为少年的挺身而出&#xff0c;而更加瑰丽。 ——《人民日报》 105. 从前序与中序遍历序列构造二叉树 力扣 题目描述&#xff1a; 给定两个整数数组 preord…

研究论文关于火灾的烟雾探测

普拉萨梅什加德卡尔 探索所有模型以选择最佳模型。 一、介绍&#xff1a; 烟雾探测器检测烟雾并触发警报以提醒他人。通常&#xff0c;它们存在于办公室、家庭、工厂等。通常&#xff0c;烟雾探测器分为两类&#xff1a; Photoelectric Smoke Detector- 设备检测光强度&#x…

Go微服务实践 - Rpc核心概念理解

概述 从0研究一下Golang已经Golang的微服务生态体系&#xff0c;Golang的微服务首先要从Rpc开始&#xff0c;在升级到Grpc&#xff0c;详细介绍这些技术点都在解决什么技术问题。 Rpc Rpc (Remote Procedure Call) 远程过程调用&#xff0c;简单的理解是一个节点请求另一个节…

LeetCode150道面试经典题--最后一个单词的长度(简单)

1.题目 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 2.示例 3.思路 通过对字符串的反转&#xff0c;转为数组开始遍历&#xff0c…

网络安全进阶学习第十五课——Oracle SQL注入

文章目录 一、Oracle数据库介绍二、Oracle和MySQL的语法差异&#xff1a;三、Oracle的数据库结构四、Oracle的重点系统表五、Oracle权限分类1、系统权限2、实体权限3、管理角色 六、oracle常用信息查询方法七、联合查询注入1、order by 猜字段数量2、查数据库版本和用户名3、查…

解析Python面向对象:从小白到面向对象大师的蜕变之路

文章目录 一 类和对象的概念二 类的认识2.1 类的定义和使用语法2.2 成员变量和成员方法 三 类和对象3.1 类和对象的关系3.2 构造方法3.3 魔术方法概述&#xff08;内置类方法&#xff09;3.4 内置方法详解 四 面向对象三大特性4.1 封装4.1.1 封装的理解4.1.2 私有成员变量和方法…

Kafka3.4 SASL/kerberos/ACL 证以及 SSL 加密连接

Kafka3.4 SASL/kerberos ACL 证以及 SSL 加密连接 序 前面我们使用 kafka3.3.1 on zookeeper 的模式进行多网段监听的 kafka 集群&#xff0c;顺便搭建起 kafkaui 后发现一些问题&#xff0c;我们 kafka 集群没有连接认证&#xff0c;万一谁知道了我们的 kafka 连接地址&…

java连接sqlserver细节处理

这个文章记录一下java连接sqlserver细节处理 &#xff0c;有其他的细节可以评论。 首先是 驱动 driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriverpom文件 将mysql的改成这个 <!-- 数据库驱动 --><dependency><groupId>com.microsoft.sqlserve…

[国产MCU]-BL602开发实例-定时器

定时器 文章目录 定时器1、BL602定时器介绍2、定时器驱动API介绍3、定时器使用实例3.1 单次计时3.2 持续计时通用定时器,用于定时,当时间到达我们所设置的定时时间会产生定时中断,可以用来完成定时任务。本文将详细介绍如何使用BL602的定时器功能。 1、BL602定时器介绍 BL6…

牛奶产业链的工业“链主品牌”利乐是如何诞生的?

瑞典的利乐公司&#xff0c;一个在乳品产业链中占据重要地位的“链主品牌”&#xff0c;通过提供创新的包装材料和解决方案&#xff0c;在全球范围内占据了显著的市场份额。利乐从不生产一滴奶&#xff0c;却赚取了中国乳业 75%的利润&#xff0c;一年创收超过 800 亿人民币。在…