详解自定义类型:结构体,位段,枚举,联合

news2024/11/19 7:26:36

目录

1.结构体

 1.1  结构的基础知识

 1.2 结构的声明

 1.3 特殊的声明

 1.4 结构的自引用 

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

 1.6 结构体内存对齐

 1.7 修改默认对齐数

 1.8 结构体传参 

2. 位段 

  2.1 什么是位段

  2.2 位段的内存分配 

  2.3 位段的跨平台问题 

  2.4 位段的应用 

3. 枚举

  3.1 枚举类型的定义 

  3.2 枚举的优点

  3.3 枚举的使用

4. 联合(共用体) 

  4.1 联合类型的定义

  4.2 联合的特点

  4.3 联合大小的计算

总结:


本篇将介绍自定义类型:结构体,,位段,枚举,联合,内容较多,请让我一一讲解


1.结构体

1.1  结构的基础知识

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

数组:数组是一组相同元素类型的集合

 1.2 结构的声明

struct tag
{
    member-list;
}variable-list;

struct是关键字  

tag自己设定的名字

member-list 成员列表

variable-list 

 例如描述一个学生:

#include <stdio.h>
struct student
{
	char name[20];
	int age;
	char sex[5];
}s1, s2, s3;
int main()
{
	struct student s4, s5, s6;
	struct student s7 = { "hulala",20,"男" };
	printf("name=%s\nage=%d\nsex=%s\n", s7.name, s7.age, s7.sex);
	return 0;
}

学生可以有姓名,年龄,性别等信息  可以打印出来

s1,s2,s3是全局变量,直接创建好了就可以使用  分号不能少

而s4,s5,s6是局部变量


 1.3 特殊的声明

//匿名结构体类型
struct
{
    int a;
    char b;
    float c;
}x;


struct
{
    int a;
    char b;
    float c;
} *p;

 注意:这些是匿名结构体类型,如图 x 和*p只能使用一次

不能再创建第二次了

上面的两个结构在声明的时候省略掉了结构体标签(tag)。
那么问题来了?

//在上面代码的基础上,下面的代码合法吗?
p = &x;

警告:
编译器会把上面的两个声明当成完全不同的两个类型。
所以是非法的。


1.4 结构的自引用 

在结构中包含一个类型为该结构本身的成员是否可以呢? 

struct Node
{
	int data;
	struct Node next;
};
//可行否?
如果可以,那sizeof(struct Node)是多少?

解析: 无法打印 因为无限大

struct Node next里面又包含了 int date 和 struct Node next

一直循环下去,根本不会打印

正确的自引用方式:

struct Node
{
	int data;  //4个字节
	struct Node* next;//指针类型要么4个字节或者8个字节

};


typedef struct
{
	int data;
	Node* next;
}Node;
//这样写代码,可行否?
#include <stdio.h>
int main()
{
	printf("%d",sizeof( Node));
}

解析:这个写法错了 Node都还没有命名 ,就有一个Node*指针变量了 

//解决方案:

 

提前声明Node 


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

有了结构体类型,那如何定义变量,其实很简单。

struct Point
{
	int x;
	int y;
}p1; //声明类型的同时定义变量p1
    p1 = { 4,5 };
    struct Point p2 = { 2,3 }; //定义结构体变量p2
#include <stdio.h>
int main()
{
	//初始化:定义变量的同时赋初值。
	struct Point p3 = { 1,2};
}

p1和p2都是全局变量,p3是局部变量 

struct Stu //类型声明
{
	char name[15];//名字
	int age;//年龄
};
#include <stdio.h>
int main()
{
	struct Stu s = { "zhangsan", 20 };//初始化
}

 双重定义:

#include <stdio.h>
struct Point
{
	int x;
	int y;
};
struct Node
{
	int data;
	struct Point p;
	struct Node* next;
}n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化
int main()
{
	struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化
}

 1.6 结构体内存对齐

我们已经掌握了结构体的基本使用了。
现在我们深入讨论一个问题:计算结构体的大小。
这也是一个特别热门的考点: 结构体内存对齐 

例子:

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

为什么参数类型都全部一样,只是类型顺序不一样。为什么所占内存都不一样呢? 

考点
如何计算?
首先得掌握结构体的对齐规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

 

解析:刚开始0的位置谁都可以上去 c1占了1个字节 

但是i占4个字节 ,它的偏移量必须为4的倍数 所以中间浪费了3个字节

 然后c2是1个字节 谁都是它的倍数 

整体大小为9 这个9不是最大成员int i的倍数 

所以必须得扩大到它的倍数 为12


  

解析: 以上题目相似

c1在0的位置中 占一个字节

c2在1的位置中 占一个字节

i   必须得在4的位置中 中间的字节浪费 占4个字节

一共8个字节 刚刚好是最大的int i 4的倍数

所以就是8


struct S3
{
	double d;//8
	char c;//1
	int i;//4
};
#include <stdio.h>
int main()
{
	printf("%d\n", sizeof(struct S3));
}

 

解析:d占8个字节 从0-7

c占一个字节 8

i占4占字节,从12开始才是它的倍数

一个刚刚好16个字节 是最大的double d 8的倍数

所以是16 


struct S3
{
	double d;//8
	char c;//1
	int i;//4
};
struct S4
{
	char c1;//1
	struct S3 s3;//16
	double d;//8
};
#include <stdio.h>
int main()
{
	printf("%d\n", sizeof(struct S4));
}

解析:跟之前类型 

唯一的重点就是 s3占16个字节 这里可以最匹配成8的倍数就可以

刚刚好32 


1.7 修改默认对齐数

之前我们见过了 #pragma 这个预处理指令,这里我们再次使用,可以改变我们的默认对齐数。

#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char c1;//1
	int i;//4
	char c2;//1
};
#include <stdio.h>
int main()
{
	printf("%d", sizeof(struct S2));
}
#pragma pack()//取消设置的默认对齐数,还原为默认

 

 解析:这里设置了对齐数为1 所以没有浪费内存

所以就是6 

用完记得恢复  #pragma pack()


1.8 结构体传参 

#include <stdio.h>
struct S
{
	int data[1000];
	int num;
};
//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);
}
int main()
{
	struct S s = { {1,2,3,4}, 1000 };
	print1(s);  //传结构体
	print2(&s); //传地址
	return 0;
}

上面的 print1 和 print2 函数哪个好些? 

答案是:首选print2函数。
原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。 

所以还是传地址

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


2. 位段 

2.1 什么是位段

位段的声明和结构是类似的,有两个不同:
1.位段的成员必须是 int、unsigned int 或signed int 。

注: char类型也可以 它也属于整形家族
2.位段的成员名后边有一个冒号和一个数字。

例如:

struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};

A就是一个位段类型。
那位段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));
}

 

图中的2 5 10 30是比特位的意思

1个int 4个字节 32个比特位

8个字节

 如果不按照位段 得需要16个字节

所以位端还是节省字节  精打细算


 2.2 位段的内存分配 

1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

 例子:

如果在一个字节里放不完,必须得重新开辟另一个字节才能 

例如 c可以将1个bit放在第1个字节里,其他4个bit刚刚好和d放一起,其实这样是错误的

必须得重新开辟字节


2.3 位段的跨平台问题 

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

总结:
跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。


2.4 位段的应用 

 

 


3. 枚举

枚举顾名思义就是一一列举。
把可能的取值一一列举。

比如我们现实生活中:
    一周的星期一到星期日是有限的7天,可以一一列举。
    性别有:男、女、保密,也可以一一列举。
    月份有12个月,也可以一一列举

这里就可以使用枚举了 


3.1 枚举类型的定义 

enum Day//星期
{
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};
enum Sex//性别
{
	MALE,
	FEMALE,
	SECRET
};
enum Color//颜色
{
	RED,
	GREEN,
	BLUE
};

以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。
{}中的内容是枚举类型的可能取值,也叫 枚举常量 。


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

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

 3.2 枚举的优点

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

 3.3 枚举的使用

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

就相当于clr=2; 

4. 联合(共用体) 

4.1 联合类型的定义

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

例如:

#include <stdio.h>
union Un
{
	char c;//1
	int i;//4
};
int main()
{
	union Un un;
	printf("%d\n", sizeof(un));
}

 

按道理应该有5个字节

但才占4个字节 

因为它是联合体 取最大的一个类型的字节 


 4.2 联合的特点

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

我们来看他们的地址:
 

它们的地址都是一样的,说明它们共用一块空间 ,并且是最大字节的类型

 

面试题:判断当前计算机的大小端存储 

 以前的做法

#include <stdio.h>
int check_sys()
{
	int a = 1;
	return *(char*)(&a);
}
int main()
{
	int a = 1;
	int ret = check_sys();
	if (ret == 1)
	{
		printf("小端");
	}
	else
	{
		printf("大端");
	}
}

现在还可以这样写:

int check_sys()
{
	union Un
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;
}

 

 刚刚好 i占4个字节 c占一个字节 u.c就是就是第一个字节


4.3 联合大小的计算

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

例如:

#include <stdio.h>
union Un1
{
	char c[5];
	int i;
};
union Un2
{
	short c[7];
	int i;
};
int main()
{
	printf("%d\n", sizeof(union Un1));
	printf("%d\n", sizeof(union Un2));
}

 

 解析: 第一个是8 可不是5 因为它只是char类型 它只代表了1个字节 不能算最大字节数

所以应该是int 的倍数 8  如果是4的话 char[5]就不够了

第二个同理;


总结:

后期博主会陆续更新

如有不足之处欢迎补充交流

看到这里的友友们,支持一下博主,来个免费三连,感谢! ! !

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

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

相关文章

2023年中国恋爱社区未来发展趋势分析:多元化盈利模式实现可持续发展[图]

恋爱社区指满足情侣之间互动、记录及娱乐需求&#xff0c;以维护情侣恋爱关系的虚拟社区。恋爱社区行业主要以线上APP的虚拟形式为用户提供相关服务&#xff0c;其业务包括情侣记录、情侣互动、情侣娱乐、公共社区、线上购物、增值服务。 恋爱社区主要业务 资料来源&#xff1…

【LeetCode】47. 全排列 II

1 问题 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,2] 输出&#xff1a; [[1,1,2], [1,2,1], [2,1,1]] 示例 2&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1…

新手如何学习嵌入式Linux?

今日话题&#xff0c;新手如何学习嵌入式Linux&#xff1f;嵌入式底层开发是一项至关重要的技能&#xff0c;广泛应用于各种嵌入式系统中&#xff0c;已经成为我们生活中不可或缺的一部分。因此&#xff0c;学习嵌入式开发变得愈发重要。我这里提供了一份嵌入式学习资料&#x…

ASO优化之增加应用APP安装量的技巧2

我们需要更新应用程序&#xff0c;并且最好以用户会注意到的方式进行更新&#xff0c;季节性或节日的更新非常适合这种情况&#xff0c;例如主题的改变&#xff0c;活动的推出或者是某种折扣。活动是能够让用户相信产品是高质量的关键。 1、应用描述也是优化的一部分。 它是应…

磁珠元器件:微小却强大的科技奇迹 | 百能云芯

在现代科技的背后&#xff0c;有着许多微小而强大的元器件&#xff0c;其中之一是磁珠元器件。尽管它们可能不如计算机芯片或太阳能电池板那样广为人知&#xff0c;但磁珠元器件在各种应用中扮演着重要角色。本文将深入探讨什么是磁珠元器件&#xff0c;它们的工作原理以及在科…

springboot生鲜交易系统springboot46

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

Python爬虫如何设置代理服务器(搭建代理服务器教程)

在Python爬虫中使用代理服务器可以提高爬取数据的效率和稳定性。本文将为您提供搭建代理服务器的详细教程&#xff0c;并提供示例代码&#xff0c;帮助您在Python爬虫中设置代理服务器&#xff0c;实现更高效、稳定的数据抓取。 Python爬虫怎么设置代理服务器&#xff08;搭建代…

【vSphere 8 自签名证书】企业 CA 签名证书替换 vSphere Machine SSL 证书Ⅰ—— 生成 CSR

目录 替换拓扑图证书关系示意图说明 & 关联博文 1. 默认证书截图2. 使用certificate-manager生成CSR2.1 创建存放CSR的目录2.2 记录PNID和IP2.3 生成CSR2.4 验证CSR 参考资料 替换拓扑图 证书关系示意图 默认情况下&#xff0c;VMCA 与 Machine SSL的关系是 本系列博文要…

剖析深度学习中的epoch与batch_size关系、代码

目录 前言1. 定义2. 代码 前言 为了区分深度学习中这两者的定义&#xff0c;详细讲解其关系以及代码 1. 定义 在 PyTorch 中&#xff0c;“epoch”&#xff08;周期&#xff09;和 “batch size”&#xff08;批大小&#xff09;是训练神经网络时的两个重要概念 它们用于控…

Scala语言入门

学习了这么久让我们来回顾一下之前的内容吧 Hadoop生态体系知识串讲 Scala编程语言 一、概述 http://scala-lang.org 专门为计算而生的语言&#xff0c;Scala将(Java后者C)面向对象设计和函数式编程结合在一起的简洁的高级编程语言。而函数式编程强调的是通过传递算子&…

统计学习方法 隐马尔可夫模型

文章目录 统计学习方法 隐马尔可夫模型基本概念概率计算问题直接计算法前向算法后向算法前向概率和后向概率 学习问题监督学习算法Baum-Welch 算法E 步M 步参数估计公式算法描述 解码问题近似算法Viterbi 算法 统计学习方法 隐马尔可夫模型 读李航的《统计学习方法》时&#x…

增加并行度后,发现Flink窗口不会计算的问题。

文章目录 前言一、现象二、结论三、解决 前言 窗口没有关闭计算的问题&#xff0c;一直困扰了很久&#xff0c;经过多次验证&#xff0c;确定了问题的根源。 一、现象 Flink使用了window&#xff0c;同时使用了watermark &#xff0c;并且还设置了较高的并行度。生产是设置了…

实时精准 自我防护 | 开源网安RASP平台能力获客户认可!

近日&#xff0c;开源网安收到了一封来自华润数科的感谢信&#xff0c;表达了对开源网安团队在网络安全工作中给予大力支持的衷心感谢。开源网安十分注重客户的需求和信任&#xff0c;客户的满意和认可是开源网安最大的追求。 在助力华润数科网络安全工作开展过程中&#xff0c…

运放供电设计

文章目录 运放供电设计如何产生负电压BUCK电路BOOST电路产生负电压FLYBUCK产生负电压 运放供电设计 注&#xff1a;使用0.1u跟10u并联 如何产生负电压 问题&#xff1a;电流小&#xff0c;使用并联方式改善&#xff0c;缺点价格贵&#xff0c;淘宝上买的都是假货ICL7662多是用…

开源情报之领英人脸情报收集,如何快速收集上亿张人脸情报

一.前言 先看应用例子&#xff1a; 残忍至极&#xff01;乌克兰用人脸识别战死俄军&#xff0c;联系母亲打“心理战” 情报机构&#xff0c;所掌握的数据&#xff0c;可以是市面上流出的任何数据&#xff0c;比如市面上泄露的领英数据&#xff0c;facebook&#xff0c;twitter&…

万能DIY预约小程序源码系统 上门预约服务小程序搭建 适用于各种预约场景 自由DIY功能模块

在这个快节奏的时代&#xff0c;预约服务已经成为了我们日常生活的一部分。从看病挂号到餐厅预订&#xff0c;从美发美容到家政服务&#xff0c;预约已经深入到了各个领域。然而&#xff0c;传统的预约方式存在着许多不便&#xff0c;如电话预约、在线填写表格等&#xff0c;不…

python小游戏:小球碰撞

创建带图形界面的游戏通常需要使用图形库。Python有很多图形库&#xff0c;其中比较流行的包括Pygame, PyOpenGL, Panda3D等。在这里&#xff0c;我将用Pygame作为示例来编写一个简单的游戏。 在运行下面的代码之前&#xff0c;请确保你已经安装了Pygame库。你可以使用以下命令…

ASCII (American Standard Code for Information Interchange)

ASCII (American Standard Code for Information Interchange)美国信息交换标准代码

Python基础--PART1

最近闲来无事&#xff0c;学习使用Python也有好几年了&#xff0c;一直没有系统的总结&#xff0c;现在有时间就写一写个人的一些拙见。 PART1. 核心语法(基础语法) 1. 变量 1.1 变量的定义 ​ 变量就是可变的量&#xff0c;对于一些有可能会经常变化的数据&#xff0c;我们…

负荷不均衡问题分析处理流程

一、负荷不均衡分析 负荷不均衡判断标准&#xff1a;4G同覆盖扇区内存在无线利用率大于50%的小区&#xff0c;且两两小区间无线利用率差值大于30%&#xff0c;判定为4G负荷不均衡扇区&#xff1b;5G同覆盖扇区内存在无线利用率大于50%的小区&#xff0c;且两两小区间无线利用率…