【C语言】自定义类型讲解

文章目录

  • 一、前言
  • 二、结构体
    • 2.1 概念
    • 2.2 定义
      • 2.2.1 通常情况下的定义
      • 2.2.2 匿名结构体
    • 2.3 结构体的自引用和嵌套
    • 2.4 结构体变量的定义与初始化
    • 2.5 结构体的内存对齐
    • 2.6 结构体传参
    • 2.7 结构体实现位段
  • 三、枚举
    • 3.1 概念
    • 3.2 定义
    • 3.3 枚举的优点
      • 3.3.1 提高代码的可读性
      • 3.3.2 防止非法值
      • 3.3.3 方便维护
    • 3.4 枚举的使用
      • 3.4.1 基础使用
      • 3.4.2 搭配switch使用
      • 3.4.3 使用typedef简化枚举类型
  • 四、联合体
    • 4.1 概念
    • 4.2 定义
    • 4.3 联合体的特点
    • 4.4 联合体的使用
      • 4.4.1 基础使用
      • 4.4.2 联合体的内存大小
    • 4.5 应用场景

一、前言

本文主要是讲解C语言中的自定义类型,包括:结构体、枚举、联合体。

二、结构体

2.1 概念

在C语言中,结构体是一种强大的数据组织工具,它允许我们将不同类型的数据组合在一起,形成一个逻辑上的整体。通过合理使用结构体,可以提高代码的可读性、可维护性和复用性。

2.2 定义

2.2.1 通常情况下的定义

struct Book
{
  char name[20];
  int price;
  char id[12];
}b1,b2;

2.2.2 匿名结构体

匿名结构体通常用于一些临时性的、不需要多次使用的数据结构。由于没有类型名称,匿名结构体变量只能在声明它的作用域内使用。

struct
{
    char name[20];
    int price;
    char id[12];
}s;

2.3 结构体的自引用和嵌套

结构体可以包含自身类型的成员,这种特性称为自引用。例如,我们可以定义一个链表节点结构体,其中包含一个指向相同结构体类型的指针:

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

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

struct Book b1 = {"C Programming", 50, "123456789012"};

这里,b1是一个Book类型的结构体变量,并且在定义时初始化了它的所有成员。如果只初始化部分成员,未初始化的成员将自动初始化为零(对于数值类型)或空字符串(对于字符数组)。

2.5 结构体的内存对齐

在C语言中,结构体的内存布局受到内存对齐规则的影响。内存对齐的目的是提高数据访问的效率。以下是结构体内存对齐的一些基本规则:

  1. 第一个成员的对齐:结构体的第一个成员放在结构体变量在内存中存储位置的0偏移处开始。
  2. 后续成员的对齐:从第二个成员往后的所有成员,都放在一个对齐数(成员的大小和默认对齐数的较小值)的整数倍的地址处。
  3. 结构体总大小的对齐:结构体的总大小是结构体的所有成员的对齐数中最大的那个对齐数的整数倍。
  4. 嵌套结构体的对齐:如果结构体中嵌套了其他结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

第二点规则的例子:

struct Example
{
    char a;      // 1字节
    short b;     // 2字节
    int c;       // 4字节
    char d;      // 1字节
};

此时,内存应该如下图对齐:

把成员调换一下

struct Example
{
    char a;      // 1字节
    int c;       // 4字节
    short b;     // 2字节
    char d;      // 1字节
};

如下图:

2.6 结构体传参

在函数调用中,结构体变量可以作为参数传递。由于结构体可能包含多个成员,直接传递结构体变量可能会导致较大的内存拷贝开销。因此,建议在传递结构体时使用指针。例如:

void printBook(struct Book* book)
{
    printf("Name: %s\n", book->name);
    printf("Price: %d\n", book->price);
    printf("ID: %s\n", book->id);
}

struct Book b1 = {"C Programming", 50, "123456789012"};
printBook(&b1);

2.7 结构体实现位段

位段是一种特殊的结构体成员,它允许我们指定成员占用的位数。位段通常用于硬件编程或需要精确控制内存布局的场景。例如:

struct S
{
    char a : 3; // a占3个bit
    char b : 4;
    char c : 5;
    char d : 4;
};

struct S s;
s.a = 7; // 二进制为0111
s.b = 15; // 二进制为1111

在这个例子中,a、b、c和d是位段成员,分别占用3、4、5和4个位。位段的空间是按照需要以4个字节(int)或者一个字节(char)的方式来开辟的。

三、枚举

3.1 概念

在C语言中,枚举(enum)是一种用户自定义的整数类型,它允许为整数值赋予有意义的名称。枚举类型通常用于表示一组相关的常量,使代码更具可读性和易维护性。

3.2 定义

默认是从0开始。

enum 枚举名 {
    枚举值1,
    枚举值2,
    枚举值3,
    ...
};

例如,以下代码定义了一个表示一周七天的枚举类型Day,也可以自定义mon的值,这样它会根据最上面的值往下递增:

enum Day{
    Mon = 1,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};

3.3 枚举的优点

3.3.1 提高代码的可读性

枚举类型通过为整数值赋予有意义的名称,使代码更易于理解和维护。例如,使用枚举类型Day时,代码可以写成:

enum Day today = Fri;

3.3.2 防止非法值

枚举类型限制了变量可以取的值,从而防止非法值的赋值。例如,today变量只能取Mon到Sun中的某个值,而不能是其他任意整数。这有助于减少潜在的错误。

3.3.3 方便维护

当需要添加新的枚举值时,只需在枚举定义中添加新的名称,而无需修改其他代码。例如,如果需要添加一个表示“节假日”的枚举值,只需在Day中添加:

enum Day{
    Mon = 1,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun,
    Holiday
};

3.4 枚举的使用

3.4.1 基础使用

enum Day{
	Mon = 1,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};

int main()
{
	enum Day today = Mon;
	printf("today is %d\n", today);
	return 0;
}

3.4.2 搭配switch使用

enum Day{
	Mon = 1,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};

int main()
{
	/*模拟键盘输入 */
	enum Day today = Mon;

	switch (today) {
		case Mon:
			printf("Today is Monday\n");
			break;
		case Tues:
			printf("Today is Tuesday\n");
			break;
		case Wed:
			printf("Today is Wednesday\n");
			break;
		case Thur:
			printf("Today is Thursday\n");
			break;
		case Fri:
			printf("Today is Friday\n");
			break;
		case Sat:
			printf("Today is Saturday\n");
			break;
		case Sun:
			printf("Today is Sunday\n");
			break;
		default:
			printf("Unknown day\n");
	}
	return 0;
}

3.4.3 使用typedef简化枚举类型

typedef enum Day{
	Mon = 1,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
}Day_t;

int main()
{
	/*模拟键盘输入 */
	Day_t today = Sat;

	switch (today) {
		case Mon:
			printf("Today is Monday\n");
			break;
		case Tues:
			printf("Today is Tuesday\n");
			break;
		case Wed:
			printf("Today is Wednesday\n");
			break;
		case Thur:
			printf("Today is Thursday\n");
			break;
		case Fri:
			printf("Today is Friday\n");
			break;
		case Sat:
			printf("Today is Saturday\n");
			break;
		case Sun:
			printf("Today is Sunday\n");
			break;
		default:
			printf("Unknown day\n");
	}
	return 0;
}

四、联合体

4.1 概念

在C语言中,联合体也叫共用体,联合体(Union)是一种特殊的数据结构,它允许在同一内存位置上存储不同类型的变量。换句话说,联合体中的所有成员都共享同一块内存空间,因此,联合体的大小是它最大成员所需的内存空间。联合体是一种有效的内存使用方式,尤其是在我们需要在不同时间点使用不同类型的数据时。

4.2 定义

在C语言中,联合体的定义使用关键字 union。与结构体不同,结构体中的每个成员都有自己的内存空间,而联合体中的所有成员共用同一块内存。其基本定义格式如下:

union Un {
    char c;
    int i;
};

在上述代码中,Un 是联合体的名字,c 和 i 是该联合体的成员。虽然它有两个成员,一个是字符型 char,另一个是整型 int,但是这两个成员是共享内存的。也就是说,它们存储的数据会覆盖彼此。

4.3 联合体的特点

  1. 共用内存:联合体的成员共享同一块内存,所有成员的起始地址相同。因此,每次只能存储一个成员的数据。修改一个成员的值会影响其他成员,因为它们共用同一块内存。
  2. 大小:联合体的大小由其最大成员的大小决定。即使联合体中有多个成员,它的大小将是最大的成员所占内存的大小。举个例子,在上述定义中,char 通常占 1 字节,int 通常占 4 字节,因此该联合体的大小通常是 4 字节(由 int 决定)。
  3. 内存优化:联合体提供了内存优化的功能,尤其适用于当某个变量需要存储不同类型的数据,但在同一时刻只需要存储其中一个数据时。例如,保存一个变量可能是整数,也可能是字符值,根据需要使用不同类型的数据时,使用联合体可以节省内存空间。

4.4 联合体的使用

4.4.1 基础使用

union Un
{
	char c;
	int i;
};


int main()
{
	union Un u;
	u.c = 'A';
	u.i = 100;
	printf("c=%c i=%d\n", u.c, u.i);
	return 0;
}

此时,c=d i=100。

分析一下代码执行过程,首先u.c = ‘A’;此时联合体的内存内容是0x41。然后,u.i = 100;假设此时是小端模式则内存的内容是0x64。联合体是共享内存的所以u.c的内容已经被覆盖掉,char类型是一个字节,所以此时这个字节里面的内容用十进制表示就是100用ascii值表示就是d。

4.4.2 联合体的内存大小

#include <stdio.h>

union Un {
    char c;
    int i;
};

int main()
{
    union Un u;
    printf("Size of union Un: %zu bytes\n", sizeof(u));
    return 0;
}

根据上述代码,输出的联合体大小通常为 4 字节(这取决于系统架构)。这是因为 int 类型通常占 4 字节,而 char 占 1 字节。由于联合体共享内存,所以联合体的大小是最大成员(int 类型)所占的字节数。

4.5 应用场景

  1. 节省内存:当我们需要存储多种数据类型,但在某一时刻只需要其中之一时,使用联合体可以有效节省内存。例如,在编写操作系统或硬件驱动时,联合体常用于存储可能是不同类型的寄存器值。
  2. 类型转换:在一些需要类型转换的场合,联合体常被用作一种简便的方式来查看和修改不同数据类型的内存表示。例如,可以用联合体来将一个 float 类型的值解释为 int 类型的位表示,反之亦然。
#include <stdio.h>

union Converter {
    float f;
    int i;
};

int main()
{
    union Converter c;

    // 将 float 类型的值存储在联合体中
    c.f = 3.14f;

    // 打印 float 类型的值
    printf("Float value: %f\n", c.f);

    // 打印相应的 int 类型位表示
    printf("Int representation (as bits): %d\n", c.i);

    // 将 int 类型值赋给联合体的 i 成员
    c.i = 1078523331;  // 通过 int 位表示修改 float 值

    // 打印修改后的 float 值
    printf("Modified float value: %f\n", c.f);

    return 0;
}

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

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

相关文章

VUE2双向绑定的原理

文章目录 VUE2双向绑定的原理1. 什么是双向绑定2. 双向绑定的原理2.1 ViewModel的重要作用2.2 双向绑定的流程 3. 双向绑定的实现3.1 data响应化处理3.2 Compile编译3.3 依赖收集 VUE2双向绑定的原理 1. 什么是双向绑定 讲双向绑定先讲单项绑定&#xff0c;啥叫单项绑定&…

入行FPGA设计工程师需要提前学习哪些内容?

FPGA作为一种灵活可编程的硬件平台&#xff0c;广泛应用于嵌入式系统、通信、数据处理等领域。很多人选择转行FPGA设计工程师&#xff0c;但对于新手来说&#xff0c;可能在学习过程中会遇到一些迷茫和困惑。为了帮助大家更好地准备&#xff0c;本文将详细介绍入行FPGA设计工程…

Mac M1 ComfyUI 中 AnyText插件安装问题汇总?

Q1&#xff1a;NameError: name ‘PreTrainedTokenizer’ is not defined ? 该项目最近更新日期为2024年12月&#xff0c;该时间段的transformers 版本由PyPI 上的 transformers 页面 可知为4.47.1. A1: transformers 版本不满足要求&#xff0c;必须降级transformors &#…

深度学习 Pytorch 神经网络的学习

本节将从梯度下降法向外拓展&#xff0c;介绍更常用的优化算法&#xff0c;实现神经网络的学习和迭代。在本节课结束将完整实现一个神经网络训练的全流程。 对于像神经网络这样的复杂模型&#xff0c;可能会有数百个 w w w的存在&#xff0c;同时如果我们使用的是像交叉熵这样…

Java 大视界 -- 深度洞察 Java 大数据安全多方计算的前沿趋势与应用革新(52)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Docker使用指南(二)——容器相关操作详解(实战案例教学,创建/使用/停止/删除)

目录 1.容器操作相关命令​编辑 案例一&#xff1a; 案例二&#xff1a; 容器常用命令总结&#xff1a; 1.查看容器状态&#xff1a; 2.删除容器&#xff1a; 3.进入容器&#xff1a; 二、Docker基本操作——容器篇 1.容器操作相关命令 下面我们用两个案例来具体实操一…

【C++】STL——list的使用

目录 &#x1f495;1.带头双向链表List &#x1f495;2.list用法介绍 &#x1f495;3.list的初始化 &#x1f495;4.size函数与resize函数 &#x1f495;5.empty函数 &#x1f495;6.front函数与back函数 &#x1f495;7.push_front,push_back,pop_front,pop_back函数…

Java面试题集合篇5:10道基础面试题

文章目录 前言41、多线程使用 ArrayList42、List 和 Set 区别43、HashSet 实现原理44、HashSet检查重复和保证数据不可重复45、BlockingQueue46、Map接口46.1、HashMap实现原理46.2、HashMap在JDK1.7和JDK1.8中不同点46.3、JDK1.7 VS JDK1.8 比较 47、HashMap的put方法流程48、…

控件【QT】

文章目录 控件QWidgetenabledgeometrysetGeometry qrcwindowOpacityQPixmapfonttoolTipfocusPolicystyleSheetQPushButtonRadio ButtionCheck Box显示类控件QProgressBarcalendarWidget 控件 Qt中已经提供了很多内置的控件了(按钮,文本框,单选按钮,复选按钮&#xff0c;下拉框…

docker pull Error response from daemon问题

里面填写 里面解决方案就是挂代理。 以虚拟机为例&#xff0c;将宿主机配置端口设置&#xff0c;https/http端口设为7899 配置虚拟机的http代理&#xff1a; vim /etc/systemd/system/docker.service.d/http-proxy.conf里面填写&#xff0c;wq保存 [Service] Environment…

linux 进程补充

环境变量 基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪 里&#xff0c;但是照样可以链接成功&#…

一文解释pytorch 中的 squeeze() 和 unsqueeze()函数(全网最详细版)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;零基础入门PyTorch框架_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 …

QT:对象树

1.概念 Qt 中的对象树是一种以树形结构组织 Qt 对象的方式。当创建一个QObject&#xff08;Qt 中大多数类的基类&#xff09;或其派生类的对象时&#xff0c;可以为其指定一个父对象&#xff08;parent&#xff09;。这个对象就会被添加到其父对象的子对象列表中&#xff0c;形…

labview通过时间计数器来设定采集频率

在刚接触labview的时候&#xff0c;笔者通常用定时里的等待函数来实现指令的收发&#xff0c;但是当用到的收发消息比较多时就出现了卡顿&#xff0c;卡死的情况&#xff0c;这是因为当用队列框架时&#xff0c;程序卡在了其中的一个分支里&#xff0c;等通过相应的延时后才可以…

2024最新前端面试题(附答案及解析)

文章目录 HTML篇1、HTML5有哪些新特性&#xff1f;2、介绍下 BFC 及其应用3、内元素和块级元素的区别&#xff1f;4、Doctype作用&#xff1f;标准模式与混杂模式如何区分&#xff1f;5、引入样式时&#xff0c;link和import的区别&#xff1f;6、介绍一下你对浏览器内核的理解…

Linux:文件系统(软硬链接)

目录 inode ext2文件系统 Block Group 超级块&#xff08;Super Block&#xff09; GDT&#xff08;Group Descriptor Table&#xff09; 块位图&#xff08;Block Bitmap&#xff09; inode位图&#xff08;Inode Bitmap&#xff09; i节点表&#xff08;inode Tabl…

Java基础面试题50题

1&#xff0c;""空字符串的作用 package com.neuedu.nineteen;public class Test {public static void main(String[] args) {String s"";for (char i a; i < d; i) {ssi;//输出abc // sis;//输出cba}System.out.println(s);} }如题所示&…

【技海登峰】Kafka漫谈系列(二)Kafka高可用副本的数据同步与选主机制

【技海登峰】Kafka漫谈系列(二)Kafka高可用副本的数据同步与选主机制 一. 数据同步 在之前的学习中有了副本Replica的概念,解决了数据备份的问题。我们还需要面临一个设计难题即:如何处理分区中Leader与Follwer节点数据同步不匹配问题所带来的风险,这也是保证数据高可用的…

GB/T 44721-2024 与 L3 自动驾驶:自动驾驶新时代的基石与指引

1.前言 在智能网联汽车飞速发展的当下&#xff0c;自动驾驶技术成为了行业变革的核心驱动力。从最初的辅助驾驶功能&#xff0c;到如今不断迈向高度自动化的征程&#xff0c;每一步都凝聚着技术的创新与突破。而在这一进程中&#xff0c;标准的制定与完善对于自动驾驶技术的规…

大语言模型的个性化综述 ——《Personalization of Large Language Models: A Survey》

摘要&#xff1a; 本文深入解读了论文“Personalization of Large Language Models: A Survey”&#xff0c;对大语言模型&#xff08;LLMs&#xff09;的个性化领域进行了全面剖析。通过详细阐述个性化的基础概念、分类体系、技术方法、评估指标以及应用实践&#xff0c;揭示了…