当前位置: 首页 > news >正文

C语言之 结构体,枚举,联合

目录

1.结构体

1.1结构的基础知识

1.2结构的声明 

 1.3 特殊的声明

1.4 结构的自引用

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

1.6 结构体内存对齐

1.7 修改默认对齐数

1.8 结构体传参

2. 位段

2.1 什么是位段

2.2位段的内存分配

2.3 位段的跨平台问题

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;

tag是自定义的,member-list是成员列表,variable-list是变量类列可有可无,可以在这里添加s1,s2等结构体变量。 

例如描述一个学生:

struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}; //分号不能丢

 1.3 特殊的声明

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

//匿名结构体类型
struct
{int a;char b;float c;
}x;
struct
{int a;char b;float c;
}a[20], *p;

 匿名结构体类型是一次性的用法,当用完这个结构体之后,这个结构体会消失。

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

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

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

1.4 结构的自引用

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

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

这种写法是错误的,如果可行,sizeof(struct Node)应该是16.

正确的自引用方式:

//代码2
struct Node
{int data;struct Node* next;
};

注意:

//代码3
typedef struct
{int data;Node* next;
}Node;
//这样写代码,可行否?
//解决方案:
typedef struct Node
{int data;struct Node* next;
}Node;

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

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

struct Point
{int x;int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
struct Stu //类型声明
{char name[15];//名字int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{int data;struct Point p;struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

1.6 结构体内存对齐

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

//练习1
struct S1
{char c1;int i;char c2;
};
printf("%d\n", sizeof(struct S1));
//练习2
struct S2
{char c1;char c2;int i;
};
printf("%d\n", sizeof(struct S2));
//练习3
struct S3
{double d;char c;int i;
};
printf("%d\n", sizeof(struct S3));
//练习4-结构体嵌套问题
struct S4
{char c1;struct S3 s3;double d;
};
printf("%d\n", sizeof(struct S4));

offsetof是一个宏,可直接用,用于计算结构体成员相较于起始位置的偏移量 

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


为什么存在内存对齐?
大部分的参考资料都是如是说的:
1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常。

2. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
问。

总体来说:
        结构体的内存对齐是拿空间来换取时间的做法。
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
        让占用空间小的成员尽量集中在一起。

//例如:
struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};

 S1和S2类型的成员一模一样,但是S1和S2所占空间的大小有了一些区别。

1.7 修改默认对齐数

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

#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{//输出的结果是什么?printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));return 0;
}

结论:
结构在对齐方式不合适的时候,我么可以自己更改默认对齐数。

1.8 结构体传参

直接上代码:

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

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

2. 位段

结构体讲完就得讲讲结构体实现 位段 的能力

2.1 什么是位段

位段的声明和结构是类似的,有两个不同:
        1.位段的成员必须是 int、unsigned int 或signed int
        2.位段的成员名后边有一个冒号和一个数字。
 比如:

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

 A就是一个位段类型。
那位段A的大小是多少?

printf("%d\n", sizeof(struct A));

那么为什么是8呢?我们接着往下看

2.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;//空间是如何开辟的?

2.3 位段的跨平台问题

   1. int 位段被当成有符号数还是无符号数是不确定的。
   2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
       器会出问题。
   3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
   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 Dayenum 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 = 5; //ok??

 4. 联合(共用体)

4.1 联合类型的定义 

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

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

4.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);

第一个输出的结果是一样的。因为联合的成员是共用一块内存空间的。

 第二个输出的结果是11223355.

 面试题:

判断当前计算机的大小端存储

两种解法:

第一种:常规解法

int check_sys()
{int a = 1;if(*(char*)&a == 1)return 1;elsereturn 0;
}int main()
{int ret = check_sys();if(ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}

  第二种解法:利用联合体的特点

int check_sys()
{union Un{char c;int i;}u;u.i = 1;return u.c;
}int main()
{int ret = check_sys();if(ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}

4.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));

 Un1的成员里面char c[5]是5,int i是4,所以最大成员是5;对齐数char[5]是1,int i 是4,所以最大对齐数是4;所以Un1的大小至少是5,但5不是4的倍数,所以sizeof(union Un1) = 8.

Un2的成员里面short c[7]是14,int i是4,所以最大成员是14;对齐数short c[7]是2,int i 是4,所以最大对齐数是4;所以Un2的大小至少是14,但14不是4的倍数,所以sizeof(union Un2) = 16. 

相关文章:

C语言之 结构体,枚举,联合

目录 1.结构体 1.1结构的基础知识 1.2结构的声明 1.3 特殊的声明 1.4 结构的自引用 1.5 结构体变量的定义和初始化 1.6 结构体内存对齐 1.7 修改默认对齐数 1.8 结构体传参 2. 位段 2.1 什么是位段 2.2位段的内存分配 2.3 位段的跨平台问题 3. 枚举 3.1 枚举类型…...

红米电脑硬盘剪切

Redmi R14 2023版固态硬盘剪切 工具准备操作结尾语 首先要说明&#xff0c;本文所说的操作不一定适合你的电脑&#xff0c;因为电子产品更新换代过快&#xff0c;你的硬盘不一定能剪切&#xff0c;在操作前一定要仔细观察硬盘的型号&#xff0c;是否为同款&#xff0c;我上了图…...

微信小程序在线预览PDF文件

需求&#xff1a;微信小程序在线预览PDF合同文件&#xff0c;加载完成后强制阅读10秒才可点击同意按钮 H5代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" cont…...

Android 工厂模式增加Type-A功能测试

Android 工厂模式增加Type-A功能测试 收到客户需求想要增加Type-A测试项来验证Type-A功能&#xff0c;具体功能实现参照如下&#xff1a; /vendor/freeme/packages/apps/FreemeFactoryTest/src/com/freeme/factory/usb/TypeAUSB.java package com.freeme.factory.usb;i…...

Web攻防06_sqlmap的使用

文章目录 参考链接&#xff1a; SQLMAP简介支持五种不同的注入模式 数据猜解-库表列数据权限操作引出权限&#xff1a;引出文件&#xff1a;引出命令&#xff08;执行命令&#xff09;&#xff1a; 提交方法-POST&HEAD&JSONPost注入cookie注入注入请求头中&#xff08;…...

C++模拟实现-----日期计算器(超详细解析,小白一看就会!)

目录 一、前言 二、日期类计算器 三、日期计算器的实现 &#x1f34e;日期计算器各个接口的实现 &#x1f350;日期计算器的需求 &#x1f349;打印当前日期&#xff08;并检查日期是否合理&#xff09; &#x1f4a6;检查日期是否合理 &#x1f4a6;日期类构造函数&#x…...

Oracle实现把B表某一字段更新到A表

1.使用SQL命令UPDATE语句。 2.使用MERGE语句。 3.使用TRIGGER触发器。 4.使用游标CURSOR和循环 使用游标和循环来将B表中的数据更新到A表中&#xff0c;从而实现了两个表数据的同步。例如下面的代码实现&#xff1a;...

CUMCM历年赛题汇总

题目来源&#xff1a; 全国大学生数学建模竞赛官网 注&#xff1a;题目和数据均可在官网下载 2021–2023年 年份题号题目2023A定日镜场的优化设计2023B多波束测线问题2023C蔬菜类商品的自动定价与补货决策2023D圈养湖羊的空间利用率2023E黄河水沙监测数据分析2022A波浪能最大…...

人间道-您到底做错了什么:正心径之您要逐渐去除外邪行为

过去的您或许在您自个身上付出&#xff0c;投入了巨大&#xff0c;重大的人力&#xff0c;物力&#xff0c;财力等各方面的重重的成本&#xff0c;但是呢&#xff0c;收获却微小的稀罕&#xff0c;微少的可怜啊。甚至于一个错误&#xff0c;就把您完全陷入到万丈深渊里面去了&a…...

Spring Boot拓展XML格式的请求和响应

在我们开发过程中&#xff0c;我们经常使用的参数绝大多少事HTML和JSON格式的请求和响应处理&#xff0c;但是我们在实际开发过程中&#xff0c;我们可能经历一些&#xff0c;比如对于XML格式的请求&#xff0c;我们在后端应该如何接收&#xff0c;并且如何将XML格式的参数变成…...

0045【Edabit ★☆☆☆☆☆】【字符数转整型】Return a String as an Integer

0045【Edabit ★☆☆☆☆☆】【字符数转整型】Return a String as an Integer language_fundamentals numbers strings Instructions Create a function that takes a string and returns it as an integer. Examples stringInt("6") // 6 stringInt("1000&q…...

数据库MySQL(六):事务

事务 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 MySQL中默认事务是自动提交的&#xff0c;当执行完一条DML语句时…...

比较浮点数时,我被绊倒了

&#x1f4e2;欢迎点赞 &#xff1a;&#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;&#x1f4e2;本文作者&#xff1a;由webmote 原创&#x1f4e2;作者格言&#xff1a;新的征程&#xff0c;我们面对的不是…...

JVM进阶(1)

一)JVM是如何运行的&#xff1f; 1)在程序运行前先将JAVA代码转化成字节码文件也就是class文件&#xff0c;JVM需要通过类加载器将字节码以一定的方式加载到JVM的内存运行时数据区&#xff0c;将类的信息打包分块填充在运行时数据区&#xff1b; 2)但是字节码文件是JVM的一套指…...

【AICFD案例操作】汽车外气动分析

AICFD是由天洑软件自主研发的通用智能热流体仿真软件&#xff0c;用于高效解决能源动力、船舶海洋、电子设备和车辆运载等领域复杂的流动和传热问题。软件涵盖了从建模、仿真到结果处理完整仿真分析流程&#xff0c;帮助工业企业建立设计、仿真和优化相结合的一体化流程&#x…...

Hadoop 请求数据长度 Requested Data length 超过配置的最大值

一、问题 现象 Spark 任务速度变慢&#xff0c;也不失败。 DataNode 内存足够 CPU 负载不高 GC 时间也不长。 查看 DataNode 日志&#xff0c;发现有些日志出现很多 Netty RPC 超时。超时的 destination 是一个 NameNode 节点&#xff0c;然后查看 NameNode 节点的日志&…...

搜索与图论:染色法判定二分图

将所有点分成两个集合&#xff0c;使得所有边只出现在集合之间&#xff0c;就是二分图 二分图&#xff1a;一定不含有奇数个点数的环&#xff1b;可能包含长度为偶数的环&#xff0c; 不一定是连通图 染色可以使用1和2区分不同颜色&#xff0c;用0表示未染色 遍历所有点&…...

磁场设备主要有哪些

磁学是物理学最古老的研究领域之一&#xff0c;目前仍然充满了生机活力。对于磁性物理的科学研究、磁性材料相关的探索来说&#xff0c;磁场设备必不可少&#xff0c;因为在外加磁场的作用下&#xff0c;样品会表现出特殊的物理性质&#xff0c;并带来了巨大的应用前景&#xf…...

【wespeaker】模型ECAPA_TDNN介绍

本次主要介绍开源项目wespeaker模型介绍 1. 模型超参数 model_args: feat_dim: 80 embed_dim: 192 pooling_func: “ASTP” projection_args: project_type: “softmax” # add_margin, arc_margin, sphere, softmax scale: 32.0 easy_margin: False 2. 模型结构 2.1 Layer…...

GPT技术的广泛使用

GPT技术的广泛使用确实引发了一些关于其潜在影响的讨论&#xff0c;包括可能导致某些职业失业以及对一些互联网公司构成竞争压力的问题。然而&#xff0c;这个问题涉及到多个方面&#xff0c;而且不容易一概而论。 潜在影响&#xff1a; 自动化任务&#xff1a; GPT等自然语言…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

[USACO23FEB] Bakery S

题目描述 Bessie 开了一家面包店! 在她的面包店里&#xff0c;Bessie 有一个烤箱&#xff0c;可以在 t C t_C tC​ 的时间内生产一块饼干或在 t M t_M tM​ 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC​,tM​≤109)。由于空间…...

Python常用模块:time、os、shutil与flask初探

一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...