【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个bitchar 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;
}

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

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

相关文章

我问了DeepSeek和ChatGPT关于vue中包含几种watch的问题,它们是这么回答的……

前言&#xff1a;听说最近DeepSeek很火&#xff0c;带着好奇来问了关于Vue的一个问题&#xff0c;看能从什么角度思考&#xff0c;如果回答的不对&#xff0c;能不能尝试纠正&#xff0c;并帮我整理出一篇不错的文章。 第一次回答的原文如下&#xff1a; 在 Vue 中&#xff0c;…

纯后训练做出benchmark超过DeepseekV3的模型?

论文地址 https://arxiv.org/pdf/2411.15124 模型是AI2的&#xff0c;他们家也是玩开源的 先看benchmark&#xff0c;几乎是纯用llama3 405B后训练去硬刚出一个gpt4o等级的LLamA405 我们先看之前的机遇Lllama3.1 405B进行全量微调的模型 Hermes 3&#xff0c;看着还没缘模型…

UbuntuWindows双系统安装

做系统盘&#xff1a; Ubuntu20.04双系统安装详解&#xff08;内容详细&#xff0c;一文通关&#xff01;&#xff09;_ubuntu 20.04-CSDN博客 ubuntu系统调整大小&#xff1a; 调整指南&#xff1a; 虚拟机中的Ubuntu扩容及重新分区方法_ubuntu重新分配磁盘空间-CSDN博客 …

在 Zemax 中使用布尔对象创建光学光圈

在 Zemax 中&#xff0c;布尔对象用于通过组合或减去较简单的几何形状来创建复杂形状。布尔运算涉及使用集合运算&#xff08;如并集、交集和减集&#xff09;来组合或修改对象的几何形状。这允许用户在其设计中为光学元件或机械部件创建更复杂和定制的形状。 本视频中&#xf…

AI作画提示词:Prompts工程技巧与最佳实践

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///计算机爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于物联网智能项目之——智能家居项目…

LLMs之DeepSeek:Math-To-Manim的简介(包括DeepSeek R1-Zero的详解)、安装和使用方法、案例应用之详细攻略

LLMs之DeepSeek&#xff1a;Math-To-Manim的简介(包括DeepSeek R1-Zero的详解)、安装和使用方法、案例应用之详细攻略 目录 Math-To-Manim的简介 1、特点 2、一个空间推理测试—考察不同大型语言模型如何解释和可视化空间关系 3、DeepSeek R1-Zero的简介&#xff1a;处理更…

二叉树——429,515,116

今天继续做关于二叉树层序遍历的相关题目&#xff0c;一共有三道题&#xff0c;思路都借鉴于最基础的二叉树的层序遍历。 LeetCode429.N叉树的层序遍历 这道题不再是二叉树了&#xff0c;变成了N叉树&#xff0c;也就是该树每一个节点的子节点数量不确定&#xff0c;可能为2&a…

详解单片机学的是什么?(电子硬件)

大家好&#xff0c;我是山羊君Goat。 单片机&#xff0c;对于每一个硬件行业的从业者或者在校电子类专业的学生&#xff0c;相信对于这个名词都不陌生&#xff0c;但是掌没掌握就另说了。 那单片机到底学的是什么呢&#xff1f; 其实单片机在生活中就非常常见&#xff0c;目前…

数据密码解锁之DeepSeek 和其他 AI 大模型对比的神秘面纱

本篇将揭露DeepSeek 和其他 AI 大模型差异所在。 目录 ​编辑 一本篇背景&#xff1a; 二性能对比&#xff1a; 2.1训练效率&#xff1a; 2.2推理速度&#xff1a; 三语言理解与生成能力对比&#xff1a; 3.1语言理解&#xff1a; 3.2语言生成&#xff1a; 四本篇小结…

数据结构:优先级队列—堆

一、优先级队列 1、优先级队列概念 优先级队列&#xff0c;听名字我们就知道他是一种队列&#xff0c;队列在前面我们已经学习过了&#xff0c;它是一种先进先出的数据结构&#xff0c;但是在特殊的情况下&#xff0c;我们我们队列中元素是带有一定优先级的&#xff0c;它需要…

2025年2月2日(网络编程 tcp)

tcp 循环服务 import socketdef main():# 创建 socket# 绑定tcp_server socket.socket(socket.AF_INET, socket.SOCK_STREAM)tcp_server.bind(("", 8080))# socket 转变为被动tcp_server.listen(128)while True:# 产生专门为链接进来的客户端服务的 socketprint(&qu…

像接口契约文档 这种工件,在需求 分析 设计 工作流里面 属于哪一个工作流

οゞ浪漫心情ゞο(20***328) 2016/2/18 10:26:47 请教一下&#xff0c;像接口契约文档 这种工件&#xff0c;在需求 分析 设计 工作流里面 属于哪一个工作流&#xff1f; 潘加宇(35***47) 17:17:28 你这相当于问用例图、序列图属于哪个工作流&#xff0c;看内容。 如果你的&quo…

Zabbix 推送告警 消息模板 美化(钉钉Webhook机器人、邮件)

目前网络上已经有很多关于Zabbix如何推送告警信息到钉钉机器人、到邮件等文章。 但是在搜索下来&#xff0c;发现缺少了对告警信息的美化的文章。 本文不赘述如何对Zabbix对接钉钉、对接邮件&#xff0c;仅介绍我采用的美化消息模板的内容。 活用AI工具可以减轻很多学习、脑力负…

何谓共赢?

A和B是人或组织&#xff0c;他们怎样的合作才是共赢呢&#xff1f; 形态1:A提供自己的身份证等个人信息&#xff0c;B用来作贷款等一些事务&#xff0c;A每月得到一笔钱。 A的风险远大于收益&#xff0c;或者B从事的是非法行为&#xff1b; 形态2:A单方面提前终止了与B的合作…

物联网 STM32【源代码形式-使用以太网】连接OneNet IOT从云产品开发到底层MQTT实现,APP控制 【保姆级零基础搭建】

物联网&#xff08;IoT&#xff09;‌是指通过各种信息传感器、射频识别技术、全球定位系统、红外感应器等装置与技术&#xff0c;实时采集并连接任何需要监控、连接、互动的物体或过程&#xff0c;实现对物品和过程的智能化感知、识别和管理。物联网的核心功能包括数据采集与监…

Redis|前言

文章目录 什么是 Redis&#xff1f;Redis 主流功能与应用 什么是 Redis&#xff1f; Redis&#xff0c;Remote Dictionary Server&#xff08;远程字典服务器&#xff09;。Redis 是完全开源的&#xff0c;使用 ANSIC 语言编写&#xff0c;遵守 BSD 协议&#xff0c;是一个高性…

架构技能(四):需求分析

需求分析&#xff0c;即分析需求&#xff0c;分析软件用户需要解决的问题。 需求分析的下一环节是软件的整体架构设计&#xff0c;需求是输入&#xff0c;架构是输出&#xff0c;需求决定了架构。 决定架构的是软件的所有需求吗&#xff1f;肯定不是&#xff0c;真正决定架构…

Linux:线程池和单例模式

一、普通线程池 1.1 线程池概念 线程池&#xff1a;一种线程使用模式。线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&#xff0c;等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价&…

maven mysql jdk nvm node npm 环境安装

安装JDK 1.8 11 环境 maven环境安装 打开网站 下载 下载zip格式 解压 自己创建一个maven库 以后在idea 使用maven时候重新设置一下 这三个地方分别设置 这时候maven才算设置好 nvm 管理 npm nodejs nvm下载 安装 Releases coreybutler/nvm-windows GitHub 一键安装且若有…

【B站保姆级视频教程:Jetson配置YOLOv11环境(六)PyTorchTorchvision安装】

Jetson配置YOLOv11环境&#xff08;6&#xff09;PyTorch&Torchvision安装 文章目录 1. 安装PyTorch1.1安装依赖项1.2 下载torch wheel 安装包1.3 安装 2. 安装torchvisiion2.1 安装依赖2.2 编译安装torchvision2.2.1 Torchvisiion版本选择2.2.2 下载torchvisiion到Downloa…