C语言中结构体,枚举,联合相关介绍

news2024/11/28 11:58:15

本次重点

1、结构体 :

(1)结构体类型的声明
(2)结构的自引用
(3)结构体变量的定义和初始化
(4)结构体内存对齐
(5)结构体传参
2、枚举:
(1)枚举类型的定义
(2)枚举的优点
(3)枚举的使用
3、联合:
(1)联合类型的定义
(2)联合的特点
(3)联合大小的计算

1、结构体

1.1   为什么要有结构体?

  在我们日常写代码中,可能会运用到生活场景,而面对一些比较复杂的问题时。比如:人们姓名和年龄、住址、号码、颜色等等,我们不可能一个一个创造变量来代替,不然那个代码量是相当多,一个人还好,如果是100、1000个人呢,那代码量就非常膨大,而这个时候我们的结构体的作用就显而易见了。

1.2  结构体的定义

struct tem
{
    
};

这就是结构体的定义,tem是随意写的,而struct是不能省的,以及注意程序结尾,那里是有一个分号的。这样正常的结构体定义就完成了。

1.3  结构体的自引用

肯定是第二个正确,有我们前面学的指针可知,要想在一个函数里引用其它函数,哪怕引用的是自身也需要用指针来接收

1.4  初用结构体

接下来我就举一个用人的名字和号码、年龄等信息,组成一个结构体:

struct Proper
{
    char name[20];
    int age;
    char number[20];
    char address[20];
};

这里面涵盖了人的姓名、年龄、电话号码、住址,其中人名我是创建了name[20]字符数组来替代,年龄我用整型的age来替代,电话号码我用了字符数组number[20]来替代,住址也是用了字符数组address[20],其中每个数组中的20都是自定义的,看你所需数据的大小。

1.5  结构体的特殊引用

左边的就是结构体的特殊形式,它比平常我们定义的结构体多了两部分。

接下来我们来看看两种方式定义的结构体:

下面一行就是对应特殊形式定义的结构体,上面一行就是平常我们定义的结构体。特殊形式相较于普通形式,在代码的书写上,比较简洁,当前我们可能看不出两者有很大的区别,当我们定义的结构体名字较长时,此时特殊形式的定义的优势就显现出来了。

1.6 结构体的嵌套

struct Proper
{
    char name[20];
    int age;
};

struct net
{
    int math;
    struct Proper p;
};

int main()
{
    struct net m = { 20, {"yang", 20} };
    return 0;
}

简单的嵌套使用就是这样。当然嵌套的次数不限,嵌套的次数越多程序可读性也就越差。

1.7 结构体内存的对齐

关于结构体对齐大家可能有点陌生,我们先来看看怎么计算结构体的大小,目前我们学过的也就是利用操作符sizeof来计算,strlen()函数是计算字符的所以在这里就不适用。

struct stu1
{
    int age;
};

struct stu2
{
    char net;
};

int main()
{
    int num1 = sizeof(struct stu1);

    int num2 = sizeof(struct stu2);

    printf("num1 = %d\n", num1);
    printf("num2 = %d\n", num2);


    return 0;
}

struct stu1
{
    int age;
    int num;
};

struct stu2
{
    char net;
    char num;
};

int main()
{
    int num1 = sizeof(struct stu1);

    int num2 = sizeof(struct stu2);

    printf("num1 = %d\n", num1);
    printf("num2 = %d\n", num2);


    return 0;
}

不管是一个还是两个,最终的结果都与类型本身所占的字节数有关。当然3个以及更多,结果也是这样。

那接下来我们来试试不同类型的组合:

struct stu1
{
    int age;
    char num;
};

struct stu2
{
    char net;
    int num;
};

int main()
{
    int num1 = sizeof(struct stu1);

    int num2 = sizeof(struct stu2);

    printf("num1 = %d\n", num1);
    printf("num2 = %d\n", num2);


    return 0;
}

此时的变化还是没有多大,我猜测可能会与char类型本身只占一个字节有关 所以接下来我们利用float和int类型试试:

struct stu1
{
    int age;
    float num;
};

struct stu2
{
    float net;
    int num;
};

int main()
{
    int num1 = sizeof(struct stu1);

    int num2 = sizeof(struct stu2);

    printf("num1 = %d\n", num1);
    printf("num2 = %d\n", num2);


    return 0;
}

此时变化依旧没有多大,所以我们接下来增加三个来试试:

struct stu1
{
    char net;
    int age;
    float num;
};

struct stu2
{
    int mar;
    float net;
    char num;
};

struct stu3
{
    float mar;
    char net;
    int num;
};

int main()
{
    int num1 = sizeof(struct stu1);

    int num2 = sizeof(struct stu2);

    int num3 = sizeof(struct stu3);

    printf("num1 = %d\n", num1);
    printf("num2 = %d\n", num2);
    printf("num3 = %d\n", num3);


    return 0;
}

大家现在就可以观察到发生了变化,我们在上面不是观察到与类型自身所占字节数有关,那为什么这里就不是9而是8呢,大家心里肯定产生了巨大疑问。首先我们观察,不管这三个类型的位置如何变化,在这里位置不是产生变化的原因,接下来我们就要引出结构体内存对齐的概念了。

官方的解答:结构体内存对齐是指当我们创建一个结构体变量时,会向内存申请所需的空间,用来存储结构体成员的内容。我们可以将其理解为结构体成员会按照特定的规则来存储数据内容。

当然这段话有点难理解,我们先来了解下结构体对齐的规则:

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

接下来我就来给你们画图了解,我就拿struct stu1来给你们解释:

首先我们看第一条规则:第一个成员不管什么类型总是从偏移量为0开始,所以看图,第一个char就从0位置哪里开始。

我们再看规则第二条除了第一个成员,其它成员要对齐到对齐数的整数倍,所以第二个成员也就是int 类型要对齐到4的整数倍,所以它只能从4开始而不能从1开始,那么有的人就会问中间空的位置怎么办,我想说的是空的位置也就是浪费了而已,我们不用管它。

最后我们看第三条规则,结构体总大小为最大对齐数,此时char的对齐数是1,int对齐数是4,float对齐数也是4,所以最大的对齐数就是4。

接下来大家看图位置站到最后也只是到了11,而我们规则是要求对齐到最大对齐数的整数倍也就是4的整数倍,所以最后结构体的总大小就是12。

第四条规则大家可以看前面我们算单个类型排位置时的规律就能理解了。

大家可以多花时间练练,练熟了就懂了。

还有一个知识点就是,当前我们vs编译器默认对齐数是8也就是这样:

我们在选类型自身对齐数时,我们是选较小的对齐数。在这里可能与第三条规则选取对齐数是相反的,但大家要牢牢记住,别记混了。

1.8 修改默认对齐数

在这里给大家介绍一个预处理命令,这个预处理命名可以改变对齐数也可以自己设定,也就是#pragma。

实际应用:

#include<stdio.h>
#pragma pack(2) 

struct stu1
{
    char net;
    int age;
    float num;
};

struct stu2
{
    int mar;
    float net;
    char num;
};

struct stu3
{
    float mar;
    char net;
    int num;
};

int main()
{
    int num1 = sizeof(struct stu1);

    int num2 = sizeof(struct stu2);

    int num3 = sizeof(struct stu3);

    printf("num1 = %d\n", num1);
    printf("num2 = %d\n", num2);
    printf("num3 = %d\n", num3);


    return 0;
}

大家也可以明显的观察上面这幅图,这里也就比上面多了一条预处理命名,但结果却发生了明显变化。

当然在这里就有同学问了,如果按照先前的要求这里不应该是6吗,怎么会是10呢。那么在这这里就是还有一个坑,那就是位置对齐的数目不能小于自身的数据类型所占的空间,也就是虽然此时的系统默认对齐数是2,但是int和float型,由于他们自身所占的数据类型大小就是4,4大于默认默认对齐数2了,所以它们的对齐数还是4,也就是说上面的图画的是错的,正确的图应该是下面这图:

1.9  结构体传参

结构体和数组传参不一样,数组传参只需要把首地址传过去,也就是数组名,而不需要取地址符号,而我们结构体就需要传地址符号,当然有一种写法不需要取地址符号如下图:

#include<stdio.h>

struct Proper
{
    char name[100];
    int age;
};

void print1(struct Proper ps)
{
    printf("%d ", ps.age);
}

void print2(struct Proper* ps)
{
    printf("%d ", ps->age);
}

int main()
{
    struct  Proper s = { "yang", 20 };
    print1(s);
    print2(&s);
    return 0;
}

当然这两种有分别的名字,第一个不需要取地址符号的是传结构体,而第二种就是传地址。

在我们实际运用过程中,还是第二种好用点,虽然第一种更能理解,但实际上当遇上复杂代码,开辟的空间就比较多,会导致程序比较冗杂。所以我更比较推荐第二种写法。

2、枚举

2.1 枚举的含义

“枚举的意思就是一一列举,将所有的情况都列举出来,那么取值的时候只能是这几种情况的一种,不能是别的。” 

枚举简单的说也是一种数据类型,只不过是这种数据类型只包含自定义的特定数据,它是一组有共同特性的数据的集合。举个例子,颜色也可以定义成枚举类型,它可以包含你定义的任何颜色,当需要的时候,只需要通过枚举调用即可,另外比如说季节(春夏秋冬)、星期(星期一到星期日)等等这些具有共同特征的数据都可以定义成枚举。

2.2 枚举类型的定义

enum day
{
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};

enum gender
{
    male,
    female
};

根据我们平成定义的语句,两个语句之间都是用分号相隔,而在枚举中都是用“ ,”来间隔,而且最后一个语句不需要用什么符号来结束,这就是枚举与我们平常定义语句的不同之处。

2.3 枚举的用法

我们先来看看枚举默认的大小是多少:

#include<stdio.h>
enum day
{
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};
int main()
{
    enum day a = Mon;
    enum day b = Tues;
    enum day c = Wed;
    enum day d= Thur;

    printf("%d ", a);
    printf("%d ", b);
    printf("%d ", c);
    printf("%d ", d);
    return 0;
}

在枚举中都是默认第一个数据为0,因为Mon位于第一个位置,所以自动被赋值为0,然后Mon下面的其它数据依次递增。当然你可以自己赋值,在开头或者中间都可以,赋值完后它依然满足上面的规律。

如下图:

#include<stdio.h>
enum day
{
    Mon,
    Tues = 100,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};
int main()
{
    enum day a = Mon;
    enum day b = Tues;
    enum day c = Wed;
    enum day d= Thur;

    printf("%d ", a);
    printf("%d ", b);
    printf("%d ", c);
    printf("%d ", d);
    return 0;
}

2.3 枚举的优点

在C语言中,枚举(enum)是一种用户定义的数据类型,它可以为一组相关的整数常量定义一个名称,使得它们可以在程序中更方便地使用。枚举的优点包括:

提高代码可读性:使用枚举可以使得代码更易于阅读和理解。例如,如果有一个表示星期的枚举类型,可以将星期几表示为1到7的整数,而不是使用不明确的数字。
减少出错概率:使用枚举可以减少在程序中输入错误数字的可能性。因为枚举类型是有限的,只能使用已定义的枚举值,而不能使用其他值。
节省内存:枚举类型实际上是整数类型的子集,因此它们通常比字符串或其他数据类型占用更少的内存。
扩展性:枚举类型可以很容易地扩展,只需要在定义枚举类型的代码中添加新的枚举值即可。这使得枚举类型非常灵活,可以根据需要进行定制。
安全性:枚举类型可以提高代码的安全性,因为它们只能使用已定义的枚举值。这有助于防止输入无效值或未定义值导致程序崩溃或产生错误结果的情况。

3、联合(共同体)

3.1 联合类型的定义

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

// 联合类型的声明
union Un
{
char c ;
int i ;
};
// 联合变量的定义
union Un un ;
// 计算连个变量的大小
printf ( "%d\n" , sizeof ( un ));

联合在目前看来用的可能比较少。

3.2 联合的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联
合至少得有能力保存最大的那个成员)。
union Un
{
int i ;
char c ;
};
union Un un ;
// 下面输出的结果是一样的吗?
printf ( "%d\n" , & ( un . i ));
printf ( "%d\n" , & ( un . c ));
// 下面输出的结果是什么?
un . i = 0x11223344 ;
un . c = 0x55 ;
printf ( "%x\n" , un . i );

这个输入至少证明了一个联合体的成员就是共用一块内存空间。

3.3 联合大小的计算

union Un1
{
char c [ 5 ];
int i ;
};
union Un2
{
short c [ 7 ];
int i ;
};
// 下面输出的结果是什么?
printf ( "%d\n" , sizeof ( union Un1 ));
printf ( "%d\n" , sizeof ( union Un2 ));

联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
就比如说Un2的大小,short数组就占了14个字节,而int 只占了4个字节,所以short占的空间是最大的,所以最终空间的大小是按short的大小决定的,而又Un2的大小不是对齐数的整数倍,也就是不是4的倍数,所以最终要变成4的倍数,所以最终就是16.
讲到这里,本次的知识点就全部讲完了,感谢大家的观看,谢谢!!!!

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

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

相关文章

干细胞液氮容器选择与使用

干细胞液氮容器的使用非常重要&#xff0c;以确保干细胞样品在冷冻和储存过程中的有效性和安全性。以下是使用干细胞液氮容器时需要注意的事项&#xff1a; 1、容器选择&#xff1a;选择合适的容器非常重要。容器应具有良好的密封性能和耐腐蚀性&#xff0c;以避免外部空气和污…

01-Zookeeper特性与节点数据类型详解

上一篇&#xff1a; 在了解Zookeeper之前&#xff0c;需要对分布式相关知识有一定了解&#xff0c;什么是分布式系统呢&#xff1f;通常情况下&#xff0c;单个物理节点很容易达到性能&#xff0c;计算或者容量的瓶颈&#xff0c;所以这个时候就需要多个物理节点来共同完成某项…

Oracle VM VirtualBox安装并下载安装CentOS7

Oracle VM VirtualBox安装并下载安装CentOS7 Oracle VM VirtualBox下载CentOS创建虚拟机 Oracle VM VirtualBox VM下载链接 https://www.oracle.com/cn/virtualization/virtualbox/ 点击链接直接下载就行&#xff0c;下载完默认安装或者更改一下安装目录。 下载CentOS http://…

服务网格概述

引言 2016 年前后&#xff0c;"服务网格"这个词出现在微服务、云计算和 DevOps 的领域。Buoyant 团队在 2016 年用这个词来解释他们的产品 Linkerd。服务网格的到来主要是由于 IT 领域内的一场风暴。开发人员开始使用多语言&#xff08;polyglot&#xff09;方法构建…

古彝文识别:文化遗产的数字化之旅

目录 &#x1f345;前言&#x1f353;古彝文介绍&#x1f353;古彝文识别的重难点&#x1f352;原籍难以获取&#xff0c;传统翻译过程繁琐&#xff0c;周期长。&#x1f352;版式多样&#xff0c;笔画相近。&#x1f352;图像质量差&#xff0c;手写识别难。&#x1f352;古彜…

第二证券:迎政策助力,新型工业化爆发,德恩精工3日涨超60%

新式工业化概念26日盘中大幅拉升&#xff0c;到发稿&#xff0c;德恩精工、精伦电子、天永智能等涨停&#xff0c;固高科技涨约8%&#xff0c;亚威股份涨逾6%&#xff0c;金自天正、创世纪涨约5%。 值得注意的是&#xff0c;精伦电子已接连5个交易日涨停&#xff0c;公司昨日晚…

Mac菜单栏图标管理工具:Bartender 5 完美兼容MacOS Sonoma 14系统

Bartender 5 是一款流行的软件程序&#xff0c;专为酒店行业的调酒师和专业人士设计。它提供了一系列功能和工具来简化酒吧或餐厅的饮料订单、库存和客户偏好的管理流程。Bartender 5 的一些主要功能包括&#xff1a; 1. 饮料配方&#xff1a;该软件包括一个全面的饮料配方数据…

计算机丢失msvcp140_1.dll的解决办法,丢失msvcp140_1.dll的原因

丢失 msvcp140_1.dll 是一个常见的错误信息&#xff0c;通常会在尝试运行某些程序时出现。msvcp140_1.dll 是一个动态链接库文件&#xff0c;它包含了许多 C标准库函数的实现&#xff0c;这些函数在许多程序中都是必需的。因此&#xff0c;如果丢失了该文件&#xff0c;程序可能…

【MySQL基础 | 中秋特辑】多表查询详细总结

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【MySQL学习专栏】&#x1f388; 本专栏旨在分享学习MySQL的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 一、多表…

如何解决跨浏览器兼容性问题?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 解决跨浏览器兼容性问题⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量…

台式多参数水质测定仪可以测哪些污水

台式污水测定仪可以测哪些污水&#xff1a; 1.废水处理厂监测&#xff1a;废水处理厂需要定期监测废水中的污染物浓度&#xff0c;包括COD、氨氮、总磷和总氮等指标。台式废水检测仪可以提供快速、准确的检测结果&#xff0c;帮助废水处理厂了解废水处理效果&#xff0c;以便进…

大数据Flink(八十七):DML:Joins之Regular Join

文章目录 DML:Joins之Regular Join DML:Joins之Regular Join Flink 也支持了非常多的数据 Join 方式,主要包括以下三种: 动态表(流)与动态表(流)的 Join动态表(流)与外部维表(比如 Redis)的 Join动态表字段的列转行(一种特殊的 Join)细分 Flink SQL 支持的

《学术小白学习之路》DTM主题动态模型构建

《学术小白学习之路》DTM主题动态模型构建 一、LDA与DTM的区别二、代码实操2.1 数据2.2 获取数据向量2.3 参数设置与模型构建2.4 结果的输出一、LDA与DTM的区别 LDA主题模型主要针对一段段的文档 可以得出每个主题,所对应主题词的词语的概率 该模型的主题概率的生成是基于文…

泰国数字加密平台Bitkub创始人到访上海和数集团

2023年9月21日&#xff0c;泰国数字加密货币交易平台Bitkub创始人兼首席执行官&#xff08;CEO&#xff09;Jirayut Srupsrisopa (Topp)先生到访上海和数集团总部。董事长唐毅先生热情会见了来宾&#xff0c;双方进行了友好深入的交流。 和数集团国际部经理晋松&#xff1b;苏州…

【Linux】计算机的软硬件体系结构

文章目录 一、冯诺依曼体系结构二、操作系统(Operator System)1.操作系统的概念2.为什么要有操作系统3.操作系统如何进行管理 三、系统调用和用户操作接口1.系统调用接口2.用户操作接口 四、计算机的软硬件体系结构 一、冯诺依曼体系结构 目前我们常见的计算机&#xff0c;如笔…

自洽可分的哈密顿系统的辛算法

本文只介绍哈密顿系统的辛算法的显式结构 不给出具体的推导过程 自洽可分的哈密顿系统的辛算法 一阶显式辛结构 二阶显式辛结构 四阶显式辛结构 全代码 import matplotlib.pyplot as plt import numpy as np from scipy.optimize import fsolve##SymplecticHamilton ##self-c…

Room Arranger for Mac: 轻松创造梦想家园的必备设计软件

你是否曾经梦想过自己动手设计理想中的家居环境&#xff1f;你是否希望通过一个简单易用的工具来实现你的设计理念&#xff1f;那么&#xff0c;Room Arranger for Mac就是你的最佳选择&#xff01; Room Arranger是一款专门为Mac用户打造的室内设计软件&#xff0c;它拥有直观…

软件测试面试题 —— 整理与解析(4)

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

搭建SpringBoot项目三种方式(超详细版)

目录 一、官网下载压缩包解压 二、通过Idea脚手架搭建 三、Spring Boot项目结构 3.1 pom.xml文件 3.2 启动类 3.3 配置文件 四、通过创建Maven项目添加依赖 一、官网下载压缩包解压 接下来我们搭建一个SpringBoot项目&#xff0c;并引入SpringMVC的功能&#xff0c;首先…

【JAVA EE】详解单点登录

作者简介 目录 1.概述 2.实现方案 2.1.分散鉴权 2.2.集中鉴权 1.概述 SSO&#xff0c;即进行一次认证&#xff0c;然后就可以访问所有子系统。很明显SSO只是一种具象化的目标而已&#xff0c;目前业内为了实现单点登录、统一鉴权&#xff0c;提出了一系列的打法。比如直接…