C语言——结构体(位段)、联合体、枚举

hello,大家好!我是柚子,今天给大家分享的内容是C语言中的自定义类型结构体、联合体以及枚举,有什么疑问或建议可以在评论区留言,会顺评论区回访哦~

一、结构体 struct

a.结构体声明

不同于数组的是,结构体中的每个成员可以是不同类型的变量;而数组是一组相同元素的集合。(一)结构的声明

例如:

//结构体声明
struct Str
{
	char name[20];
	char sex[20];
	int num;
	int age;
};//注意分号不能丢!!!

(二)结构体变量的创建和初始化

#include<stdio.h>
//结构体声明
struct Str
{
	char name[20];
	char sex[20];
	int num;
	int age;
};//注意分号不能丢!!!
int main()
{
	struct Str s = { "丁鸣","女",2024001,18 };	//注意成员的顺序,不要乱!
	printf("name:%s\n", s.name);
	printf("sex:%s\n", s.sex);
	printf("num:%d\n", s.num);
	printf("age:%d\n", s.age);
	return 0;
}

(三)结构体的特殊声明

 先看代码:

//匿名结构体
struct
{
	int a;
	int b;
	int c;
}s;
struct
{
	int a;
	int b;
	int c;
}a[100],*p;

 上述两种结构体在声明的时候省略了结构体标签,那如果将第一个结构体s的地址直接给p,能不能行的通呢??

//匿名结构体
struct
{
	int a;
	int b;
	int c;
}s;
struct
{
	int a;
	int b;
	int c;
}*p;
int mian()
{
	p = &s;//是否合法?
	return 0;
}

 如果你将代码放在VS上你就会发现它会直接报错;第一,编译器会把上边两个声明当成完全不同的两个类型,所以是非法的;第二,匿名结构体类型,如果没有对结构体类型重命名的话,基本上只能用一次。

(四)结构体的自引用

在结构体中包含一个类型为该结构体本身行不行???

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

如果可以的话,那么sizeof(struct Node)的大小为多少呢?所以,这么写是错误的。

为什么?

因为一个结构体中再包含一个同类型的结构体变量,这样的话结构体变量的大小就会无穷大,是不合理的。

正确的自引用方式如下:

//正确的自引用
struct Node
{
	int data;
	struct Node* next;//存放节点地址
};

 我们再来看一段代码:

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

这样的代码是否可行?

当然是大错特错啦,你这个Node命名还没完成,你就提前开始使用,肯定是错的。

正确的应该这么用:

//正确的typedef
typedef struct Node
{
	int data;
	struct Node* next;
}Node;

b.结构体内存对齐

(一)对齐原则:
1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处

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

   对齐数=编译器默认的一个对齐数与该成员变量大小的较小值

-Vs中默认的值为8 
-Linux中gcc没有默认对齐数,对齐数就是成员自身的大小

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

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

(二)为什么存在内存对齐?

1.平台原因(移植原因): 
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数; 否则抛出硬件异常。 

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

假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

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

所以,为了让我们既满足对齐,又满足节省空间,我们要尽可能的让占用空间小的成员集中在一起         

(三) 修改默认对齐数

 #pragma 这个预处理指令,可以改变编译器的默认对齐数。

//#pragma
#pragma pack(1)	//设置默认对齐数1、4、8....
struct s
{
	char c1;
	int i;
	char c2;
};
#pragma pack()	//取消默认对齐数
int main()
{
	printf("%d\n", sizeof(s));
	return 0;
}

(四)结构体传参

//结构体传参
struct S
{
	int data[1000];
	int num;
};
//①
void print1(struct S t)
{
	printf("%d %d\n", t.data[3], t.num);
}
//②
void print2(const struct S* pt)
{
	printf("%d %d\n", pt->data[3], pt->num);
}
int main()
{
	struct S s = { {1,2,3,4,5,6},10};
	print1(s);
	print2(&s);
	return 0;
}

一般我们选择第二种方法print2传参,不管是在空间上还是在性能上都是比较好的选择!!但是为了安全起见还是会加上const。

原因:函数传参的时候,参数时需要压栈的,会有时间和空间上的系统开销。

           如果传递一个过大的结构体变量,参数压栈的系统开销较大导致性能下降。

结论:结构体传参的时候,要传结构体的地址。

(五)结构体实现位段

什么是位段?(位表示:二进制位)

位段是基于结构体的

位段的声明和结构是类似的,有两个不同
1.位段的成员必须是int、unsigned int 或signed int,在C99中位段成员的类型也可以选择其他类型。

2.位段的成员名后边有一个冒号和一个数字

//位段
struct A
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;	//一共47个比特位,两个int型
};
int main()
{
	printf("%d\n", sizeof(A));	//8
	return 0;
}

位段的出现其实就是为了节省空间的。

位段的内存分配:

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

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

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

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

二、结构成员访问操作符

结构体成员依据结构体变量类型的不同,一般有2种访问方式,一种为直接访问,一种为间接访问,相同的成员名称依靠不同的变量前缀区分。

a.结构体成员的直接访问

直接访问应用于普通的结构体变量,直接访问使用结构体变量名.成员名

b.结构体成员的间接访问

间接访问应用于指向结构体变量的指针,间接访问使用(*结构体指针名).成员名或者使用结构体指针名->成员名

三、联合体(共用体)union

a.联合体类型的声明

//联合体
union u
{
	char c;
	int u;
};
int main()
{
	union u uu;
	printf("%zd\n", sizeof(uu));
	printf("%zd\n", &uu);
	printf("%zd\n", &(uu.c));
	printf("%zd\n", &(uu.u));

	return 0;
}

联合体的成员共用一块空间 ,两个成员不同时使用。

b.联合体的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小。

c.联合体大小的计算

联合提的大小至少是最大成员的大小。(X)

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

//计算
union U
{
	char c[5];	//5
    //按数组中的元素来算:1 8 1
	int a;	//4
    //4 8 4
};
int main()
{
	printf("%zd\n", sizeof(union U)); //8
	return 0;
}

联合体也是存在对齐的。 

d.相同成员的结构体和联合体的对比

e.联合体的应用:

使用联合体是可以节省空间的,比如,我们要搞一个活动,要上线一个礼品兑换单,礼品兑换单中有三种商品 : 图书、杯子、衬衫。

每一种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。

图书:书名、作者、页数
杯子:设计
衬衫:设计、可选颜色、可选尺寸

struct gift_1ist
{
	//库存量、价格、商品类型
	int stock_num;
	double price;
	int item_type;
	union {
		//图书:书名、作者、页数
		struct
		{
			char title[20];
			char author[20];
			int num_pages;
		}book;
		//杯子:设计
		struct
		{
			char design[30];
		}mug;
		//衬衫:设计、可选颜色、可选尺寸
		struct
		{
			char design[30];
			int color;
			int sizes;
		}shirt;
	}item;
};

举例二: 

union U
{
	int n;//4
	struct S
	{
		char c1;
		char c2;
		char c3;
		char c4;
	}s;//4
};
int main()
{
	union U u = { 0 };
	u.n = 0x11223344;
	//拿出每个字节里的内容,巧妙利用联合体成员占用同一个空间
	printf("%x %x %x %x\n", u.s.c1, u.s.c2, u.s.c3, u.s.c4);
	return 0;
}

四、枚举 enum

a.枚举类型的声明

枚举就是可以一一例举,把可能的取值一一列举。

//枚举 enum
enum Sex
{
	//性别
	MALE,
	FEMALE,
	SECRET
};
int main()
{
	printf("%d\n", MALE);
	printf("%d\n", FEMALE);
	printf("%d\n", SECRET);
	return 0;
}

b.枚举类型的优点

我们可以使用#define定义常量,为什么非要使用枚举?
枚举的优点: 
1.增加代码的可读性和可维护性 

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

3.便于调试,预处理阶段会删除#define定义的符号

4.使用方便,一次可以定义多个常量

5.枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用

今天的分享就先到这里,我们下期不见不散!!!拜拜~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

16:00面试,16:06就出来了,问的问题过于变态了。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到2月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

C++对象内存模型布局详解

目录 本文主要内容如下&#xff1a; 最后还有一些问题&#xff1a; 一、理解虚函数表 二、对象模型概述 三、继承下的C对象模型 单继承&#xff1a; 多继承&#xff1a; 一般的多继承&#xff08;非菱形继承&#xff09;&#xff1a; 菱形继承&#xff1a; 五、虚继承…

如何创建一个VUE3项目并使用Element UI插件

1.确保已经安装了Node&#xff1a; win R 打开控制面板&#xff0c;输入“node -v”回车。出现版本号信息&#xff0c;则安装成功&#xff0c;否则请移步安装。 Node.js安装及环境配置&#xff08;简单易懂&#xff01;&#xff09;_building: c:\program files\nodejs\node…

基于SSM的学科竞赛管理系统。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的学科竞赛管理系统。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringMvcMybatisVueLayuiElemen…

我的第②个出海工具站 - 2024年50个出海工具站计划

为了大家更好的使用各种出海工具。我上线了一版 出海工具导航 站点&#xff0c;经常使用的可以收藏下&#xff0c;我文内使用的网站都集成在了这里&#xff0c;非常使用。 随着AIGC的到来&#xff0c;2024年到了海外工具回暖的一年。今年计划上线50款出海工具站计划&#xff0c…

嵌入式工程师函数变量,常用的命名规则(参考学习)

很多工程师不注重平时编码习惯&#xff0c;比如命名规则&#xff0c;一会大写、一会小写&#xff0c;一会中文拼音&#xff0c;一会下划线等&#xff0c;导致自己写的代码自己都看不懂了。 今天就来分享一点关于软件代码常见的几种命名规则。 匈牙利命名法 匈牙利命名法广泛应…

西安雁塔未来人工智能计算中心算力成本分析

先看一例旧闻&#xff1a;西部“最强大脑”落户雁塔——30亿亿次超算能力助力创新之城建设 其中提到一期算力为 300PFLOPS FP16&#xff08;每秒30亿亿次半精度浮点计算&#xff09;&#xff0c;项目总投资约为19亿元。 这个算力是什么概念呢&#xff1f; 我们以深度学习训练中…

使用sunshine和moonlight实现远程游戏串流

过年回家想要打游戏&#xff0c;但是苦于家里没有电脑&#xff0c;又没办法把电脑搬回去&#xff0c;于是想到了使用串流的方式。 实现串流的软件有多种&#xff1a; moonlight。因为仅实现了 NVIDIA 的游戏串流协议&#xff0c;所以只支持 N 卡。Steam Link。支持 steam 的游…

第五十回 插翅虎枷打白秀英 美髯公误失小衙内-mayfly-go:web 版 linux、数据库等管理平台

晁盖宋江和吴用到山下迎接雷横上山&#xff0c;宋江邀请雷横入伙&#xff0c;雷横以母亲年事已高为由拒绝了。 雷横回到郓城&#xff0c;听李小二说从东京新来了个表演的叫白秀英&#xff0c;吹拉弹唱跳&#xff0c;样样精通&#xff0c;于是雷横和李小二一起到戏院去看演出。…

什么是AJAX?它的运用场景有哪些?

文章目录 前言一、什么是AJAX二、AJAX原理是什么三、为什么需要AJAX四、AJAX的使用五、AJAX的应用场景 前言 AJAX 即 Asynchronous Javascript And XML&#xff08;异步JavaScript和XML&#xff09;&#xff0c;是指一种创建交互式网页应用的网页开发技术。 AJAX 是一种用于创…

首尔之春在线资源最新电影1080p高清

打开下面这个链接就可以看到 首尔之春在线资源最新电影1080p高清 如果链接打不开&#xff0c;就复制下面的网址到浏览器打开 https://www.zhufaka.cn/liebiao/A09504AE3BF8BD06 用阿里云盘下载&#xff0c;下载完成之后&#xff0c;用迅雷播放 首尔之春在线资源最新电影10…

NVMe管理命令为何不用SGL?-2

在IO数据传输中&#xff0c;是否选择SGL可以根据自身场景的需要。SGL提供的是一种高效且灵活的方式来描述非连续的内存区域&#xff0c;这对于现代高性能存储系统至关重要&#xff0c;尤其是在处理大数据块或者随机小I/O操作时具有明显优势&#xff1a; 高效的数据传输&#xf…

【OpenGL编程手册09】颜色和光照

目录标题 一、说明二、物理概念三、OpenGL处理办法四、创建一个光照场景 一、说明 在前面的教程中我们已经简要提到过该如何在OpenGL中使用颜色(Color)&#xff0c;但是我们至今所接触到的都是很浅层的知识。本节我们将会更深入地讨论什么是颜色&#xff0c;并且还会为接下来的…

Minio容器化部署并整合SpringBoot

1、启动minio容器 docker run -p 9000:9000 -p 9090:9090 --name minio -d --restartalways -e MINIO_ACCESS_KEYminio -e MINIO_SECRET_KEYminio -v /usr/local/minio/data:/data -v /usr/local/minio/config:/root/.minio minio/minio server /data --console-addr…

文件操作与IO(3) 文件内容的读写——数据流

目录 一、流的概念 二、字节流代码演示 1、InputStream read方法 第一个没有参数的版本&#xff1a; 第二个带有byte数组的版本&#xff1a; 第三个版本 搭配Scanner的使用 2、OutputStream write方法 第一个版本&#xff1a; 第二个写入整个数组版本&#xff1a; …

16 PyTorch 神经网络基础【李沐动手学深度学习v2】

要想直观地了解块是如何工作的&#xff0c;最简单的方法就是自己实现一个。 在实现我们自定义块之前&#xff0c;我们简要总结一下每个块必须提供的基本功能。 将输入数据作为其前向传播函数的参数。 通过前向传播函数来生成输出。请注意&#xff0c;输出的形状可能与输入的形…

python一张大图找小图的个数

python一张大图找小图的个数 一、背景 有时候我们在浏览网站时&#xff0c;发现都是前端搞出来的一张张图&#xff0c;我们只能用盯住屏幕的小眼睛看着&#xff0c;很累的统计&#xff0c;这个是我在项目中发现没办法统计&#xff0c;网上的教程很多&#xff0c;都不成功&…

构建信息蓝图:概念模型与E-R图的技术解析

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

C# 中 Interpreter 用于解释执行代码的工具

在 C# 中&#xff0c;Interpreter 是一个用于解释执行代码的工具&#xff0c;它提供了一种在运行时动态解释和执行 C# 代码的方式。Interpreter 类位于 Microsoft.CodeAnalysis.CSharp.Scripting 命名空间中&#xff0c;它允许你通过编写代码字符串来执行 C# 代码。 下面是一些…

毫秒生成的时间戳如何转化成东八区具体时间

假设现在有一个时间是1709101071419L 后端代码实现 Java代码&#xff08;东八区时间&#xff09; 在Java代码中&#xff0c;我们将时区从UTC调整为东八区&#xff08;UTC8&#xff09;&#xff1a; import java.time.Instant; import java.time.ZoneId; import java.time.Z…