C语言自定义类型(结构体,枚举,联合):

news2024/12/27 12:35:26

大家好久不见,今天我们来学习一下C语言中的自定义类型:

C语言的自定义类型包括:结构体,枚举和联合,接下来大家跟我来一起认识一下这三种类型。

目录

1. 结构体

1.1.1 结构体类型的声明

1.1.2 结构的特殊声明

1.1.3 结构体的自引用

1.2. 结构体的内存对齐

1.2.1 对⻬规则

1.2.2 为什么有内存对齐

1.2.3 修改对齐数 

1.3 结构体实现位段

1.3.1 什么是位段

1.3.2 位段的内存分配 

1.3.3 位段的问题

2. 联合体

2.1.1 联合体类型的声明

2.2 联合体的特点

2.2.1 联合体大小的计算 

2.3 联合体的使用案例

 3. 枚举类型

3.1 枚举类型的声明

2.2 枚举类型的优点 


1. 结构体

结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
1.1.1 结构体类型的声明
struct tag
{
    member- list ;
}variable- list ;

 例如我们要写一个学生的结构体,包含学生的年龄,性别,姓名和学号,

struct Stu
{
     char name[ 20 ]; // 名字
     int age; // 年龄
     char sex[ 5 ]; // 性别
     char id[ 20 ]; // 学号
}; // 分号不能丢
1.1.2 结构的特殊声明

在声明结构的时候,可以不完全的声明。

struct
{
    int a;
    char b;
    float c;
}x;
struct
{
    int a;
    char b;
    float c;
}a[ 20 ], *p;

 当创建无命名的结构体时,我们称该结构体为匿名结构体,那这种结构体有没有什么问题呢?

p=*x; 

当执行p=*x代码时,编译器会将这个结构体认成是两种不同的类型,但实际上这是同一个结构体。

所以对于匿名结构体来说,应该将所有该结构体变量在声明时创建。 

1.1.3 结构体的自引用
struct Node
{
     int data;
     struct Node next ;
};

 那 么将结构体作为它自身的一个成员 这样行不行得通呢?

如果这样的话,在计算结构体类型的大小时就会像套娃一样无穷的计算下去。所以是不可行的。

那么如果我们想让结构体作为成员,应该怎么来声明呢?

我们知道,指针的大小只与机器本身有关,如果我们使用指向结构体的指针作为成员,不就解决这个问题了吗?

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

 那如果我们在定义结构体时对结构体进行了typedef重命名,在指针部分可以使用重命名的名字吗?

答案是不行的,因为结构是在成员创建之后才进行的重命名,所以要用原名。

typedef struct
{
    int data;
    Node* next;    //错误
}Node;
 
typedef struct
{
    int data;
    struct Node* next;     //正确
}Node;
1.2. 结构体的内存对齐
我们已经掌握了结构体的基本使⽤了。现在我们深⼊讨论⼀个问题:计算结构体的⼤⼩。
结构体是一种类型,所以可以通过sizeof()来计算结构体的大小。那么问题来了,结构体的大小应该怎么计算呢?
我们接下来来学习一下结构体的内存对齐规则。
1.2.1 对⻬规则
⾸先得掌握结构体的对⻬规则:
1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处。
2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。

- VS 中默认的值为 8
- Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
对其数与机器也有关,一般在32位机器下是4,64位机器下是8。
我们来通过图来详细了解一下:
struct S1
{
    char c1;
    int i;
    char c2;
};
比如对于上面的结构体S1,

 因为结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处。所以结构体中第一个char类型占内存大小为1,位置从0到1,而对于int来说,它的对齐数为4,位置应该从4到8,对于第二个char类型来说,它的对其数为1,所以可以从位置为8的位置紧接着,位置为8到9。所有的成员类型都花完了,在计算结构体大小时,还要考虑最大对其数,在该结构体中为4,所以结构体的大小为4的整数倍,对与该结构体来说,大小应该为12。

1.2.2 为什么有内存对齐
1. 平台原因 (移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因:
数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。
总体来说:结构体的内存对⻬是拿空间来换取时间的做法。
所以我们在创建结构体时应该 让占⽤空间⼩的成员尽量集中在⼀起,对于上文的结构体S1,可以写成
struct S1
{
    char c1;
     char c2;
    int i;
};

此时结构体的大小为8,节省了空间。

1.2.3 修改对齐数 
#pragma 这个预处理指令,可以改变编译器的默认对⻬数。
# include <stdio.h>
# pragma pack(1) // 设置默认对⻬数为 1
struct S
{
    char c1;
    int i;
    char c2;
};
# pragma pack() // 取消设置的对⻬数,还原为默认
int main ()
{
    printf ( "%d\n" , sizeof ( struct S));
    return 0 ;
}
1.3 结构体实现位段
1.3.1 什么是位段
位段的声明和结构是类似的,有两个不同:
1. 位段的成员必须是 int unsigned int signed int ,在C99中位段成员的类型也可以选择其他类型。
2. 位段的成员名后边有⼀个冒号和⼀个数字。
例如下面是一个位段:
struct A
{
    int _a: 2 ;
    int _b: 5 ;
    int _c: 10 ;
    int _d: 30 ;
};

位段与结构体十分相似,那么位段的大小又是多少呢?下面我们来学习一下位段的内存分配。

1.3.2 位段的内存分配 
1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
我们现在创建一个位段:
struct S
{
    char a: 3 ;
    char b: 4 ;
    char c: 5 ;
    char d: 4 ;
};
struct S s = { 0 };
s.a = 10 ;
s.b = 12 ;
s.c = 3 ;
s.d = 4 ;

在位段声明时冒号后带的数字为该成员的空间大小,而数据是在创建变量后才能赋值。

 

这就是S位段的内存大小,当一个字节内剩余空间大于下一个成员的大小时,下一个成员将会存放到这个字节中。

1.3.3 位段的问题
1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
4.  位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位 置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。
所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。

2. 联合体

2.1.1 联合体类型的声明
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。
但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。
给联合体其中⼀个成员赋值,其他成员的值也跟着变化。
union Un
{
    char c;
    int i;
};
int main ()
{
// 联合变量的定义
    union Un un = { 0 };
// 计算连个变量的⼤⼩
    printf ( "%d\n" , sizeof (un));
    return 0 ;
}

 结算的结果是4,那为什么是这个结果?我们继续往下看。

2.2 联合体的特点
联合的成员是共⽤同⼀块内存空间的,这样⼀个联合变量的⼤⼩,⾄少是最⼤成员的⼤⼩(因为联合⾄少得有能⼒保存最⼤的那个成员)。
对于结构体和联合体:
struct S{
    char c;
    int i;
};
 
union A{
    char c;
    int i;
};
 

在联合体中,成员公用一块空间,所以改变一个成员的值时,其他成员的值也会跟着改变。

2.2.1 联合体大小的计算 

根据上文,我们可以知道:联合体的大小至少是最大成员的大小,而当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。

2.3 联合体的使用案例

联合体的使用可以节省空间,

⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、⻚数
杯⼦:设计
衬衫:设计、可选颜⾊、可选尺⼨
那我们不耐⼼思考,直接写出⼀下结构:

 

struct gift_list      //不使用联合体
{
    //公共属性
    int stock_number;//库存量
    double price; //定价
    int item_type;//商品类型

    //特殊属性
    char title[20];//书名
    char author[20];//作者
    int num_pages;//⻚数

    char design[30];//设计
    int colors;//颜⾊
    int sizes;//尺⼨
};


struct gift_list      //使用联合体
{
    int stock_number;//库存量
    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 colors;//颜⾊
            int sizes;//尺⼨
        }shirt;
    }item;
};

 3. 枚举类型

3.1 枚举类型的声明
枚举顾名思义就是⼀⼀列举。
把可能的取值⼀⼀列举。
⽐如我们现实⽣活中:
⼀周的星期⼀到星期⽇是有限的7天,可以⼀⼀列举
性别有:男、⼥、保密,也可以⼀⼀列举
⽉份有12个⽉,也可以⼀⼀列举
三原⾊,也是可以意义列举
这些数据的表⽰就可以使⽤枚举了。

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 = 2,
    GREEN = 4,
    BLUE = 8
}; 

2.2 枚举类型的优点 
我们可以使⽤ #define 定义常量,为什么⾮要使⽤枚举?
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符⽐较枚举有类型检查,更加严谨。
3. 便于调试,预处理阶段会删除 #define 定义的符号
4. 使⽤⽅便,⼀次可以定义多个常量
5. 枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤

 

那么我们今天的学习就到这里啦,我们下次再见。

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

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

相关文章

nginx的重定向(rewrite)

nginx的重定向&#xff08;rewrite&#xff09; location 匹配 location匹配的就是后面的URI /wordpress 192.168.60.20/wordpress location匹配的分类和优先级 1、精确匹配 location / 对字符串进行完全匹配&#xff0c;必须完全符合 2、正则匹配 ^~ 前缀匹配&#x…

为本地化准备营销材料的几个步骤

为本地化准备营销材料涉及几个关键步骤&#xff0c;以确保内容在文化上合适、语言上准确&#xff0c;并与目标受众相关。以下是五个基本步骤&#xff1a; 进行市场调查 了解目标市场至关重要。进行深入研究&#xff0c;以收集有关目标地区受众的文化细微差别、消费者行为、地…

脑启发设计:人工智能的进化之路

编者按&#xff1a;你可以用左手&#xff08;不常用的那只手&#xff09;的小指与食指拿起一件物品么&#xff1f; 试完你是不是发现自己竟然可以毫不费力地用自己不常用的手中&#xff0c;两根使用频率相对较低的手指&#xff0c;做一个不常做的动作。这就是人类大脑不可思议…

MySQL之聚簇索引和非聚簇索引

1、什么是聚簇索引和非聚簇索引&#xff1f; 聚簇索引&#xff0c;通常也叫聚集索引。 非聚簇索引&#xff0c;指的是二级索引。 下面看一下它们的含义&#xff1a; 1.1、聚集索引选取规则 如果存在主键&#xff0c;主键索引就是聚集索引。如果不存在主键&#xff0c;将使…

高校搭建AIGC新媒体实验室,创新新闻教育教学模式

高校作为人才培养的重要阵地&#xff0c;必须紧跟时代步伐&#xff0c;不断创新教育教学模式&#xff0c;提升跨界融合育人水平&#xff0c;通过AIGC新媒体实验室探索创新人才培养模式。AIGC新媒体实验室不仅能够高效赋能高校宣传媒体矩阵&#xff0c;也可以助力教学实践与AIGC…

常见问题记录

conda操作 conda精确查找某个包的版本 conda list 包名下载源 -i https://pypi.mirrors.ustc.edu.cn/simple/conda查看下载源 conda config --show channels下载torch conda install pytorch1.13.0 torchvision0.14.0 torchaudio0.13.0 cpuonlyconda环境没有名字 利用vsc…

改变conda创建虚拟环境时的默认路径(C盘)

①C:\Users\Lenovo 找到C盘中用户目录下的.condarc文件 ②打开.condarc文件后&#xff0c;添加并修改.condarc 中的 env_dirs 环境路径&#xff0c;按顺序第⼀个路径作为默认存储路径&#xff0c;也就是我的E盘&#xff08;需要你先在E盘中新建文件夹envsE&#xff09;。修改完…

算法体系-25 第二十五节:窗口内最大值或最小值的更新结构

一 滑动窗口设计知识点 滑动窗口是什么&#xff1f; 滑动窗口是一种想象出来的数据结构&#xff1a; 滑动窗口有左边界L和有边界R 在数组或者字符串或者一个序列上&#xff0c;记为S&#xff0c;窗口就是S[L..R]这一部分 L往右滑意味着一个样本出了窗口&#xff0c;R往右滑意味…

【免费可视化工具】助力风电行业智能化管理

在绿色能源日益成为全球共识的今天&#xff0c;风电作为清洁能源的重要组成部分&#xff0c;正以前所未有的速度发展。然而&#xff0c;随着风电场规模的扩大和数量的增加&#xff0c;如何高效、直观地管理和监控风电资源成为了一个亟待解决的问题。 而山海鲸可视化这款免费可…

使用微信开发者工具连接gitee

编写代码 打开微信开发者工具 编写小程序代码 提交代码 在微信开发者工具提交代码到gitee仓库的步骤&#xff1a; 1.在gitee创建仓库&#xff0c;得到仓库url 2.微信开发者工具设置远程仓库 点击版本管理-->点击设置-->网络和认证-->认证方式选择 使用用户名和…

Windows系统安装分布式搜索和分析引擎Elasticsearch与远程访问详细教程

文章目录 前言系统环境1. Windows 安装Elasticsearch2. 本地访问Elasticsearch3. Windows 安装 Cpolar4. 创建Elasticsearch公网访问地址5. 远程访问Elasticsearch6. 设置固定二级子域名 前言 本文主要介绍如何在Windows系统安装分布式搜索和分析引擎Elasticsearch&#xff0c…

怎么录制电脑内部声音?好用的录音软件分享,看这篇就够了!

如何录制电脑内部声音&#xff1f;平时使用电脑工作&#xff0c;难免会遇到需要录音的情况。好用的录音软件有很多&#xff0c;也有部分录屏工具也支持录音功能。 那么如何录制电脑内部声音呢&#xff1f;本文整理了几个录制电脑内部声音的方法&#xff0c;如果你需要在电脑上录…

全网都在疯传的最新蓝海风口项目!

最近全网都在疯传这种视频&#xff0c;想必兄弟们都见到过了&#xff01; 大家看这个号&#xff0c;1天的时间&#xff0c;2个作品&#xff0c;第2个直接就爆了&#xff0c;昨天看点赞还是3.8w&#xff0c;今天已经10w了&#xff0c;这是妥妥的风口啊&#xff01; 大家有没有想…

Qt 进程间通信(一)——QSharedMemory共享内存

QSharedMemory共享内存 序言环境理论—逻辑理解实战—代码读取示例写入示例 序言 讲讲Qt的共享内存吧&#xff0c;巩固下 环境 msvc2022 Qt5.15 参考文档&#xff1a;https://doc.qt.io/qt-5/qsharedmemory.html 理论—逻辑理解 看下面前&#xff0c;你需要将共享内存看成…

数字研发·驱动变革 | 2024达索系统装备行业数字化研发专题研讨会成功举办

2024年6月28日&#xff0c;由百世慧举办的“数字研发驱动变革|2024达索系统装备行业数字化研发专题研讨会”在达索系统&#xff08;重庆&#xff09;智能制造创新中心成功举办。 随着全球制造业向着智能化、数字化转型&#xff0c;我国工业装备行业也面临着转型升级的压力和机遇…

【代码随想录】【算法训练营】【第57天】 [卡码99]岛屿数量 [卡码100]岛屿的最大面积

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 卡码网。 day 57&#xff0c;周三&#xff0c;再ding一下~ 题目详情 [卡码99] 岛屿数量 题目描述 卡码99 岛屿数量 LeetCode类似题目200 岛屿数量 解题思路 前提&#xff1a; 思路&#xff1a; 重点&#…

集团型企业组织架构复杂,业务线多,如何进行高效费用管控?

企业管理中流行这样一句话&#xff1a;“企业转型&#xff0c;财务先行”。对集团型企业而言&#xff0c;当今的发展形势下&#xff0c;通过财务战略全面转型、最终撬动企业价值提升&#xff0c;是一件难而正确的事情。 集团企业具有经营规模大、产业链多、分支机构多、地域跨度…

亚马逊SC账号升级VC账号的新浪潮已然席卷整个电商界!

当前电商市场竞争激烈&#xff0c;亚马逊卖家追求业务增长。现有Amazon SC账号卖家有机会升级为VC账号&#xff0c;提供重要机遇。 SC账号是亚马逊常见卖家类型&#xff0c;为众多个人和企业提供销售平台。而VC账号则代表与亚马逊更紧密的合作关系&#xff0c;享有更多优惠、广…

phpexcel导入导出

前言&#xff1a; 如果你到处的excel软件打开有问题&#xff0c;下面有介绍解决办法 导入 1. composer init 初始化 2. 下载phpspreadsheet 这里需要注意php版本&#xff0c;需要大于7.2 composer require phpoffice/phpspreadsheet3. 编写代码 <?php require vendo…