自定义类型:结构体、位段、枚举、联合

news2024/11/25 12:59:40

文章目录

  • 前言
  • 1. 结构体
    • a. 关键字:struct
    • b. 结构体基础知识:
    • c. 结构体声明:
    • d. 特殊的声明
    • e. 结构的自引用
    • f. 结构体变量的定义与初始化
    • <font color = red>g. 结构体内存对齐
    • h. 结构体传参
  • 2. 位段
    • a. 设计目的:节省空间
    • b. 什么是位段
    • c. 位段的内存分配
    • d. 位段的跨平台问题
  • 3. 枚举
    • a. 关键字:enum
    • b. 枚举的定义
    • c. 枚举的优点
    • d. 枚举的使用
  • 4. 联合(共用体)
    • a. 关键字:union
    • b. 定义
    • c.联合的特点
    • d.联合的大小


前言

在这里插入图片描述


1. 结构体

a. 关键字:struct

b. 结构体基础知识:

i. 成员变量:结构体是一些值的集合,这些值称为成员变量,每个成员可以是不同类型的变量
ii. 与其类似的,数组:一组相同元素的集合

c. 结构体声明:

i.

struct student //结构体名称
{
	//成员列表
	char name[20];
	int age;
}stu1;//变量名

d. 特殊的声明

i. 匿名结构体类型

  1. 适用范围:仅用一次,后不再用
  2. 使用:
struct
{
	int a;
	float b;
};
  1. 解释:没有名称可读性差,调用的时候会比较麻烦,故一般适用于只用一次的场景

e. 结构的自引用

i. 数据结构:描述的是数据在内存中的存储和组织结构

  1. 线性数据结构:顺序表
    a) 包含数据域和指针域,指针域存放着指向下一个位置的指针,这就是一种自引用
  2. 树形数据结构:树、二叉树等
    ii. 自引用
  3. 结构体中除了数据(数据域)之外还需要包括一个指向下一位置的指针(指针域)
  4. 引用方式:
struct Node
{
	int a;
	struct Node* next;//存放执行下一个结构体的指针
};

f. 结构体变量的定义与初始化

i. 变量定义

//声明类型的同时定义结构体变量
struct student
{
	int a;
	float b;
}p1;

//定义结构体变量
struct student p2;

int main() {
	//定义结构体变量
	struct student p3;
	return 0;
}

ii. 初始化

  1. 顺序初始化(默认)
struct student
{
	int a;
	float b;
}p1 = {12,32.5};//定义的同时初始化


struct student p2 = {18,99.9};//初始化

int main() {

	struct student p3 = {12,13.4};//初始化
	return 0;
}
  1. 指定顺序初始化
struct student
{
	int age;
	char name[20];
};

int main() {

	struct student p3 = { .name = "张三",.age = 18};
	return 0;
}
  1. 复杂类型
struct point
{
	int x;
	int y;
};

struct student
{
	int age;
	char name[20];
	struct point p;
	struct student* next;
};

int main() {
	struct student s = { 12,"张三",{12,23},NULL };
	return 0;
}

g. 结构体内存对齐

i. 引入

  1. 例子:
struct stu1 {
	char a;
	char b;
	int c;
};

struct stu2 {
	char a;
	int c;
	char b;
};

int main() {
	printf("%d\n", sizeof(struct stu1));
	printf("%d\n", sizeof(struct stu2));
	return 0;
}
  1. 结果:
    在这里插入图片描述
  2. 问题:两个结构体中,只有成员的顺序发生了变化,为什么会导致大小发生改变?

ii. offsetof(宏,可以直接使用)

  1. 功能:用于计算结构体成员相较于其实位置的偏移量(相较于起始位置偏移了几个字节,偏移量就是几)
  2. 使用:
    a) 介绍:
    在这里插入图片描述

 i) 头文件:<stddef.h>
 ii) 参数:两个参数,类型与对象
 iii) 返回值:返回偏移量
b) 示例:

struct stu1 {
	char a;
	char b;
	int c;
};

struct stu2 {
	char a;
	int c;
	char b;
};

int main() {
	printf("%d\n", offsetof(struct stu1,a));
	printf("%d\n", offsetof(struct stu1,b));
	printf("%d\n", offsetof(struct stu1, c));
	return 0;
}

结果:在这里插入图片描述
在这里,我们清楚的看到了每个元素存储的起始位置,那么,它们为什么会这样存储?

iii. 如何计算

  1. 对齐规则:
    a) 起始位置
    第一个成员在与结构体变量偏移量为0的地址处
    b) 对齐数
    其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的值为8
    c) 结构体总大小
    结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
    d) 结构体嵌套
    如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

iv. 为什么要对齐

  1. 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
    定类型的数据,否则抛出硬件异常。
  2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
    原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
  3. 总结:拿空间换取时间
  4. 怎样设计结构体
    a) 让占用空间小的成员尽量 在一起

v. 修改默认对齐数

  1. 可以使用#pragma 这个预处理指令,可以改变我们的默认对齐数。
  2. 一般设置为2的某次方
  3. 例如:
#pragma pack(8)//设置默认对齐数为8
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
	//输出的结果是什么?
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

h. 结构体传参

i. 传值调用(少用)

  1. 缺点
    a) 函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
    如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的
    下降。
    ii. 传输地址(传址调用)
    iii.示例:
struct stu {
	char name[20];
	int age;
};

struct stu s = {"张三",12};

void print1(struct stu s) {
	printf("%s %d\n",s.name,s.age);
}

void print2(struct stu* s) {
	printf("%s %d\n",s->name,s->age);
}

int main() {
	print1(s);
	print2(&s);
	return 0;
}

结果:
在这里插入图片描述

2. 位段

a. 设计目的:节省空间

b. 什么是位段

i. 位段的声明和结构类似,但有两个不同

  1. 位段的成员:只有int、unsigned int 、signed int 、char(属于整形家族)
  2. 成员名:后面可以有冒号和数值
  3. 举例
struct stu
{
	char a : 1;
	char b : 2;
	char c : 6;
	char d : 3;
};

struct stu s = { 1,2,3,4 };

int main() {
	printf("%d\n",sizeof(s));
}

结果:
在这里插入图片描述
赋值详情:
在这里插入图片描述

在这里插入图片描述

ii. 位段的位指的是二进制位

  1. 位段成员冒号后面的数字就是它占用了几个bit位的空间

c. 位段的内存分配

i. 成员位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型

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

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

iv. 在VS怎么使用

  1. 单个字节中,从右往左由低到高使用
  2. 如果剩余空间不够就舍弃

d. 位段的跨平台问题

. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
舍弃剩余的位还是利用,这是不确定的。

3. 枚举

a. 关键字:enum

b. 枚举的定义

i. 枚举的取值默认从0开始
ii. 枚举可能的值是可以修改的,可以在初始时为其设置值
iii. 枚举类型占4个字节
iv. 初始化的时候设定了一个值,则其后的值默认从此开始增加
举例:

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

enum Sex//性别
{
	MALE,
	FEMALE,
	SECRET
};

enum Color//颜色
{
	RED,
	GREEN,
	BLUE
};

c. 枚举的优点

增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量

d. 枚举的使用

enum Color//颜色
{
	RED,
	GREEN,
	BLUE
};
int main() {
	printf("%d %d %d\n",RED,GREEN,BLUE);
	return 0;
}

结果:
在这里插入图片描述
2.

enum Color//颜色
{
	RED = 5,
	GREEN,
	BLUE
};
int main() {
	printf("%d %d %d\n",RED,GREEN,BLUE);
	return 0;
}

结果:
在这里插入图片描述

4. 联合(共用体)

a. 关键字:union

b. 定义

union stu 
{
	int age;
	char sex[2];
}p1;

union stu p2;

int main() {
	printf("%d\n",sizeof(p1));
	return 0;
}

结果:
在这里插入图片描述

c.联合的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
这个从上一步的结果就可以看出

d.联合的大小

至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
举例:

union stu
{
	int age;
	char name[7];
}p1;

union stu p2;

int main() {
	//按类型,最大的是int类似,4个字节,所以需要时4的倍数
	//char数组大小为7,至少要比7大
	printf("%d\n", sizeof(p1));
	return 0;
}

结果:
在这里插入图片描述

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

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

相关文章

黑马程序员RabbitMQ入门到实战教程【高级篇】学习笔记

目录 一、发送者的可靠性 1.1、生产者重试机制 1.2、生产者确认机制 1.3、实现生产者确认 1.3.1、开启生产者确认 1.3.2、定义ReturnCallback 1.3.3、定义ConfirmCallback 二、MQ的可靠性 2.1、数据持久化 2.1.1.交换机持久化 2.1.2、队列持久化 2.1.3、消息持久化…

正则表达式 Regular Expression学习

该文章内容为以下视频的学习笔记&#xff1a; 10分钟快速掌握正则表达式_哔哩哔哩_bilibili正则表达式在线测试工具&#xff1a;https://regex101.com/, 视频播放量 441829、弹幕量 1076、点赞数 19330、投硬币枚数 13662、收藏人数 26242、转发人数 2768, 视频作者 奇乐编程学…

红包雨高并发压测记录(200台机器压测实录)

压测5000线程10秒内循环5次&#xff0c;5台2核心4线程的机器&#xff0c;QPS2500&#xff0c;每台机器需要承受500的QPS 压测10000线程10秒内循环5次&#xff0c;10台2核心4线程的机器&#xff0c;QPS5000&#xff0c;每台机器需要承受500的QPS 压测200000线程10秒内循环5次&am…

Overloud TH-U Complete for Mac:演绎您的音乐世界

Overloud TH-U Complete for Mac是一款功能强大的吉他谱曲软件&#xff0c;可以让您在Mac电脑上轻松进行吉他模拟、录音和混音等操作&#xff0c;创作属于自己的音乐作品。 Overloud TH-U Complete for Mac提供了丰富的吉他模拟和音效库&#xff0c;涵盖了多种吉他放大器、箱体…

SpringCloud(一)Eureka、Nacos、Feign、Gateway

文章目录 概述微服务技术对比 Eureka服务远程调用服务提供者和消费者Eureka注册中心搭建注册中心服务注册服务发现Ribbon负载均衡负载均衡策略饥饿加载 NacosNacos与Eureka对比Nacos服务注册Nacos服务分集群存储NacosRule负载均衡服务实例权重设置环境隔离 Nacos配置管理配置热…

GD32F103x 定时器

1. 定时器的基本介绍 STM32的定时器主要分为三种&#xff1a;高级定时器、通用定时器、基本定时器。 即&#xff1a;高级定时器具有捕获/比较通道和互补输出&#xff0c;死区时间&#xff0c;通用定时器只有捕获/比较通道&#xff0c;基本定时器没有以上两者。 1. 基本定时…

【图像处理】【应用程序设计】加载,编辑和保存图像数据、图像分割、色度键控研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

minikube如何设置阿里云镜像以及如何解决dashboard无法打开的解决方案_已设置图床

minikube如何设置阿里云镜像以及如何解决dashboard无法打开的解决方案 minikube dashboard报错 considerconsider-Dell-G15-5511:~$ minikube dashboard &#x1f914; 正在验证 dashboard 运行情况 ... &#x1f680; 正在启动代理... &#x1f914; 正在验证 proxy 运行…

LabVIEW工业虚拟仪器的标准化实施

LabVIEW工业虚拟仪器的标准化实施 创建计算机化的测试和测量系统&#xff0c;从计算机桌面控制外部测量硬件设备&#xff0c;以及在计算机屏幕上显示的类似仪器的面板上查看来自外部设备的测试或测量数据&#xff0c;所有这些都需要虚拟仪器系统软件。该软件允许用户执行所有这…

链表经典面试题(四)

分割链表 1.题目2.详细的图文分析3.详细的注释和代码 1.题目 2.详细的图文分析 我们会定义4个指向分割链表的指向指针,分别来表示两个链表的头和尾 并且将数据一一的放到两个链表中,最后再将它们串起来,代码中有详细注释. 3.详细的注释和代码 public class Partition {public…

【C语言】模拟实现strcat

strcat在小白看来是一个比较陌生的库函数&#xff0c;但也要牢牢掌握 目录 介绍&#xff1a;模拟实现&#xff1a; 介绍&#xff1a; str代表字符串&#xff0c;那么cat代表什么&#xff1f; 他代表Catenate&#xff0c;连接的缩写&#xff0c;也就是追加字符串的意思 代码示例…

海信电视U8KL使用体验:参数卷,画质技术也独有!

每个家庭成员对电视都有不同需求&#xff0c;如何能做到兼顾&#xff1f;看似需求众口难调&#xff0c;其实一台海信电视就能满足所有啦。 海信电视的参数不仅是最卷的&#xff0c;同时画质技术还是国内独有的&#xff0c;能把这样一台优秀的电视搬回家&#xff0c;无论电影、…

拒绝水文!八大排序(三)【适合初学者】快速排序

文章目录 快速排序递归实现霍尔法优化 挖坑法前后指针法 快速排序非递归 大家好&#xff0c;我是纪宁&#xff0c;这篇文章将向大家介绍非常有名气的一款排序&#xff1a;快速排序 回忆到我们刚开始学习C语言的时候。经常会使用到一个库函数&#xff1a; qsort函数 &#xff0…

【JVM】垃圾回收(GC)详解

垃圾回收&#xff08;GC&#xff09;详解 一. 死亡对象的判断算法1. 引用计数算法2. 可达性分析算法 二. 垃圾回收算法1. 标记-清除算法2. 复制算法3. 标记-整理算法4. 分代算法 三. STW1. 为什么要 STW2. 什么情况下 STW 四. 垃圾收集器1. CMS收集器&#xff08;老年代收集器&…

kubernetes教程-基本学习环境配置

kubernetes教程-基本学习环境配置 安装必要工具 kubectl Kubernetes的命令行工具&#xff0c; kubectl, 允许你在Kubernetes集群中运行命令. 你可以用kubectl来部署应用, 检查和管理集群资源, 并查看日志。有关更多信息&#xff0c;包括 kubectl的全部命令完整列表, 查看 ku…

string类的使用方式的介绍

目录 前言 1.什么是STL 2. STL的版本 3. STL的六大组件 4.STL的缺陷 5.string 5.1 为什么学习string类&#xff1f; 5.1.1 C语言中的字符串 5.2 标准库中的string类 5.3 string类的常用接口的使用 5.3.1 构造函数 5.3.2 string类对象的容量操作 5.3.3 string类对象…

【iptables 实战】06 iptables网络防火墙实验

一、现状说明 在上一节中&#xff0c;我们将两个网段的机器&#xff0c;通过中间机器的网络转发&#xff0c;能达到互通。再来回顾一下这个网络连接的图 这一节&#xff0c;我们将通过设置机器B的iptables规则&#xff0c;来做一些防火墙实验 机器A模拟公网的一台服务器&#…

【C++进阶之路】C++11(上)

文章目录 一、列表初始化1.{}2.initializer_list 二、声明1.auto2.deltype 三、右值与左值1.基本概念2.应用场景1.左值引用2.右值引用3.完美转发4.万能引用 四、新增默认成员函数五、lambda表达式1.基本语法1.1捕捉列表1.2参数列表1.3返回类型1.4函数体 2.底层原理 总结 一、列…

GPT系列模型解读:GPT-1

GPT系列 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是一系列基于Transformer架构的预训练语言模型&#xff0c;由OpenAI开发。以下是GPT系列的主要模型&#xff1a; GPT&#xff1a;GPT-1是于2018年发布的第一个版本&#xff0c;它使用了12个Transformer…

2021-06-20 51单片机基于STC89C52RC的简易秒表的设计与实现(外部中断1和2)

缘由基于STC89C52RC的简易秒表的设计与实现_编程语言-CSDN问答 1.功能要求&#xff1a; K1键做启动停止秒表&#xff08;外部中断0&#xff09;&#xff0c;K2键做秒表归零&#xff08;外部中断1&#xff09;&#xff0c;4位数码管动态扫描显示&#xff0c;定时范围改成0到00…