详细介绍关于自定义类型:结构体、枚举、联合【c语言】

news/2024/4/28 1:11:43/文章来源:https://blog.csdn.net/qq_73478334/article/details/128419636

文章目录

  • 结构体
    • 结构体的声名
      • 特殊的声明
    • 结构成员的类型
    • 结构的自引用
    • 结构体变量的定义和初始化
    • 结构体内存对齐
      • 修改默认对齐数
    • 结构体变量访问成员
    • 结构体传参
    • 结构体实现位段(位段的填充&可移植性)
      • 位段的内存分配
      • 位段的跨平台问题
  • 枚举
    • 枚举类型的定义
    • 枚举的优点
    • 枚举的使用
  • 联合体
    • 联合类型的定义
    • 联合的特点
    • 联合大小的计算

在这里插入图片描述

结构体

结构的基础知识
结构是一些值得集合,这些值被称为成员变量。结构的每个成员可以是不同类型的变量。

结构体的声名

struct tag
{
member-list;
}variable-list;

struct是关键字,tag 是标签名,标签名是可以根据需求改变的。 member-list是成员列表 ,struct tag是结构体类型

struct stu
{char name[20];int age;char sex [10];float score;
};

特殊的声明

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

  • 匿名结构体类型

在声明的时候省略掉了结构体标签(tag)

struct
{char c;int i;char ch;double d;
} s;

虽然下面两个声名的成员变量相同,但是编译器会把下面的两个声明当成完全不同的两个类型


struct
{char c;int i;char ch;double d;
} s;struct
{char c;int i;char ch;double d;
}* ps;int main()
{ps = &s;return 0;
}

结构成员的类型

结构的成员可以是标量 ,数组 ,指针 ,甚至可以其他结构体。

结构的自引用

代码一:

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

代码二:

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

代码一是错误的 ,代码二是正确的 。 在结构体里,不是包含同类型的结构体变量, 而是包含同类型的结构体指针。

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

struct stu
{char name[20];int age;char sex[10];float score;
}s4, s5 ; 
int main()
{struct stu   s1 , s2 ,s3;return 0;
}

在这里插入图片描述

s1 ,s2 ,s3 是局部变量 ,s4,s5是全局变量

struct stu
{char name[20];int age;char sex[10];float score;
};
int main()
{struct stu   s1 = { "zhangsan" , 20 , "nan" , 95.5f };struct stu  s2 = { "旺财", 21, "保密", 59.5f };printf("%s %d %s %f\n", s2.name, s2.age, s2.sex, s2.score);return 0;
}

结构体内存对齐

结构体对齐规则

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

 struct S1{char c1;int i;char c2;};printf("%d\n", sizeof(struct S1));

在这里插入图片描述

第一个成员(c1)在与结构体变量偏移量为0的地址处
i的大小是4个字节,默认对齐数是8 ,取其较小值(4)就是i的对齐数
偏移量4才是i的对齐数(4)的倍数,这里需要浪费3个字节
i在内存中占4个字节 ,偏移量的范围是4到7
c2的自身大小是1 ,默认对齐数是8 ,取其较小值(1)为c2的对齐数
8偏移量是对齐数(1)的倍数,这里不需要浪费空间
根据结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
结构体s1 总共占了9个字节 ,结构体s1的最大对齐数是4 ,所以还需要浪费3个字节才能是4的倍数, 所以s1的总大小是12个字节


struct S3
{double d;char c;int i;
};

在这里插入图片描述

结构体嵌套结构体

struct S3
{double d;char c;int i;
};
struct S4
{char c1;struct S3 s3;double d;
};
printf("%d\n", sizeof(s4));

在这里插入图片描述

第一个成员(c1) 放到偏移量为0的地址处
如果嵌套了结构体,s3要对齐自己的最大对齐数(8)的整数倍处
s3 的偏移量的范围是8到23,总共占了16个字节
d 的自身大小是8 ,默认对齐数是8 ,取其较小值(8)就是对齐数
偏移量24是d的对齐数(8)的整数倍, 不需要浪费空间
d的偏移量的范围是24到31 ,占了8个字节
结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
s4的整体大小是32个字节 ,s4中的最大对齐数是8 ,32是8的倍数


那为什么要内存对齐?

  • 平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
    定类型的数据,否则抛出硬件异常。
  • 性能原因:
    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
    原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
    问。

总结: 结构体的内存对齐是拿空间来换取时间的做法

修改默认对齐数

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

#pragma pack(2)//设置默认对齐数为2
struct S
{char c1;int i;char c2;
};

#pragma pack()//取消设置的默认对齐数,还原为默认
struct S
{char c1;int i;char c2;
};

结构体变量访问成员

结构体变量的成员是通过点操作符(.)访问的
点操作符接收两个操作数


代码一:

struct S
{int a;char c;
};struct P
{double d;struct S s ;float f;};
int main()
{struct P p = { 5.5 , { 100 ,'b'} , 3.14f };printf("%d %c\n",p.s.a , p.s.c);return 0;
}

结构体传参

#include <stdio.h>
struct S
{int a;char c;
};struct P
{double d;struct S s ;float f;};
void Print1( struct P sp)
{printf("%lf %d %c\n", sp.d, sp.s.a, sp.s.c);
}
void Print2(struct P *  p1)
{printf("%lf %d %c\n", (*p1).d , (*p1).s.a , (*p1).s.c );printf("%lf %d %c\n",  p1->d , p1->s.a , p1->s.c);}
int main()
{struct P p = { 5.5 , { 100 ,'b'} , 3.14f };Print1(p);//传值调用 Print2(&p); //传址调用return 0;
}

代码二中的(*p1)使用结构体指针访问指向对象的成员


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

所以结构体传参的时候,要传结构体的地址

结构体实现位段(位段的填充&可移植性)

  • 位段的成员名后边有一个冒号和一个数字
  • 位段的成员可以是 int (unsigned int 、signed int )或者是 char (属于整形家族)类型
  • 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
  • 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
struct A
{//4个字节 - 32bitint _a : 2;//_a 成员占2个bit位int _b : 5;//_b 成员占5个bit位int _c : 10;//_c 成员占10个bit位//15//4个字节 - 32bitint _d : 30;//_d 成员占30个bit位
};
int main()
{printf("%d\n", sizeof(struct A));//8return 0;
}

位段的内存分配

struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};
int main()
{struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;return 0;
}

在这里插入图片描述

转换成16进制
在这里插入图片描述
在这里插入图片描述

一个数据在使用的时候,从低位向高位使用,从右向左使用的(仅适配vs)


位段和结构体相比来说 ,位段可以达到和结构体一样的效果,而且位段还可以更好的节省空间 ,但是位段有个很严重的问题 :跨平台

位段的跨平台问题

  • int 位段被当成有符号数还是无符号数是不确定的。
  • 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
    器会出问题。
  • 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  • 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

枚举

把可能的取值一一列举。

枚举类型的定义

enum Color  //枚举类型 
{RED,    //枚举常量GREEN,  //枚举常量BLUE   //枚举常量
};

enum Color 是枚举类型。
{}中的内容是枚举类型的可能取值,也叫 枚举常量
这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值

enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};

枚举的优点

  • 增加代码的可读性和可维护性
  • 和#define定义的标识符比较枚举有类型检查,更加严谨。
  • 防止了命名污染(封装)
  • 便于调试
  • 使用方便,一次可以定义多个常量

枚举的使用

//声明枚举类型
enum Color
{RED,GREEN,BLUE 
};
int main()
{  enum Color c = BLUE;return 0;
}

联合体

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

联合类型的定义

//联合类型的声明
union Un
{char c;int i;
};
//联合变量的定义
int main()
{union Un u = { 0 };u.i = 1000;u.c = 100;return 0;
}

联合的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联
合至少得有能力保存最大的那个成员)

union Un
{char c;int i; 
};
int main()
{union Un u;printf("%d\n", sizeof(u));return 0;
}

在这里插入图片描述

联合大小的计算

联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

union Un
{char a[5];int i;
};
int main()
{union Un u;printf("%d\n", sizeof(u));return 0;
}

char 的对齐数是1
i 的对齐数是4
根据 联合的大小至少是最大成员的大小(5)
5不是最大对齐数(4)的整数倍
所以要浪费三个字节
总共大小是8个字节


在这里插入图片描述
如果你觉得这篇文章对你有帮助,不妨动动手指给点赞收藏加转发,给鄃鳕一个大大的关注
你们的每一次支持都将转化为我前进的动力!!!

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

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

相关文章

微信小程序入门

目录 一&#xff0c;简介 二&#xff0c;小程序开发环境搭建 1.申请账号 2.安装开发工具 3.小程序工具使用 三&#xff0c;目录结构以及json配置 1.目录结果 2.json配置 3.JSON 语法 4.WXML 5.wxss 6.JS 逻辑交互 四&#xff0c;小程序宿主环境 1.程序与页面 2.组件…

String 字符串

String 基本介绍 String 应该是 Java 中最常用的一个对象&#xff0c;他不是八种基本数据类型的其中之一&#xff0c;但是随便翻了一下项目代码&#xff0c;用 String 定义的变量超过百分之八十。 public final class Stringimplements java.io.Serializable, Comparable<…

自己整理的vue实现生成分享海报(含二维码),看着网上的没实现

大家好&#xff0c;我是雄雄。 前言 相信大家在许多的场景下&#xff0c;看到过这样的案例。 当我们在某购物app上看好一件商品&#xff0c;想分享给别人时&#xff0c;app会给我们生成一张海报&#xff0c;我们将其保存在手机里面转发给其他人达到分享。当我们逛CSDN的时候&…

12.25日周报

周报 代码行数&#xff1a; 周一 704 周二 481 周三 571 周四 589 周五 595 周六 520 周日 537 遇到的问题&#xff1a; 没用过的方法AtomicInteger Insert Proto currentTimeMillis RequestParam BufferedReader UriComponents RestTemplate OSS 不清楚在…

Windows和Mac系统实现本地部署WebPageTest工具

在项目开发或者测试的过程中&#xff0c;由于没有上线&#xff0c;我们在公网上无法访问我们的网站&#xff0c;但同时我们又需要查看浏览器性能&#xff0c;这样我们就需要在本地部署WebPageTest工具以协助进行性能测试 具体实现步骤&#xff1a; Windows系统&#xff1a; …

【高级篇04】MySQL逻辑架构

文章目录第四章&#xff1a;逻辑架构逻辑架构SQL执行流程数据库缓冲池第四章&#xff1a;逻辑架构 逻辑架构 第一层&#xff1a;连接层。客户端访问MySQL服务器&#xff0c;首先建立TCP连接&#xff0c;经过三次握手建立连接成功后&#xff0c;MySQL服务器对TCP传输过来的账号…

PHP开发工具PhpStorm v2022.3——完全支持PHP 8.2

PhpStorm是一个轻量级且便捷的PHP IDE&#xff0c;其旨在提高用户效率&#xff0c;可深刻理解用户的编码&#xff0c;提供智能代码补全&#xff0c;快速导航以及即时错误检查。可随时帮助用户对其编码进行调整&#xff0c;运行单元测试或者提供可视化debug功能。 PhpStorm v20…

excel数据处理技巧:组合函数统计产品批号

这是一个看似普通的编号问题&#xff0c;可竟然动用了TEXT和SUMPRODUCT两个重量级的函数共同出手才得以解决。以往遇到的编号问题&#xff0c;大多数都是COUNTIF的拿手好戏&#xff0c;但是今天这个问题COUNTIF完全插不上手&#xff0c;来看看模拟的数据吧。 如图所示&#xff…

循环神经网络-基础篇Basic-RNN

循环神经网络-基础篇Basic-RNN 我们把全连接网络也叫做稠密网络DNN&#xff0c;其中X1到X8是不同样本的特征 而本文介绍的循环神经网络RNN主要处理的是具有序列关系的输入数据&#xff0c;即前面的输入和后面的输入是有关系的。例如天气&#xff0c;股市&#xff0c;金融数据和…

ES6 模块化、webpack、@ 代表src目录的设置

文章目录webpackSource Map 代表src目录的设置ES6 模块化要求默认 导出默认导入按需 导出、导入混合使用直接导入 并执行模块中的代码webpack 默认 约定&#xff1a; 自定义 打包的 入口与出口 const path require(path) // 导入node.js中 专门操作路径的模块 module.expor…

哺乳时宝宝一边吃奶,另一边却自动流出来,这是怎么回事?

别人眼中的母乳喂养只是简单地把宝宝抱在怀里&#xff0c;让宝宝吃饱&#xff0c;超级简单。事实上&#xff0c;有很多母乳喂养。“麻烦事”比如母乳不足、堵奶、乳腺炎等&#xff0c;甚至更多“简单”漏奶会让宝宝头疼。有些妈妈很幸运&#xff0c;不知道什么是漏奶&#xff0…

基于Java+SpringBoot+vue等疫情期间网课管理系统详细设计和实现

博主介绍&#xff1a;✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取联系&#x1f345;精彩专栏推荐订阅收藏&#x1f447;&…

Git Bash Here和RStudio软件的问题解决

Git Bash Here和RStudio软件的问题解决 文章目录Git Bash Here和RStudio软件的问题解决0、 写在前面1、Git软件在任务栏图标空白2、RStudio软件2.1 警告信息InormalizePath(path.expand(path),winslash,mustWork)2.2 incomplete final line found by readTableHeader on报错3、…

为啥devc++程序运行正确返回不为0?而返回了一个特别大的数,详解。

例如运行以下程序: #include #include typedef char ElemType; typedef struct BiTNode{ char data; struct BiTNode *lchild; struct BiTNode *rchild; int DescNum;}BiTNode ,*BiTree; void CreateBiTree(BiTree *T) { char ch; scanf("%c",&ch); if(ch ){…

力扣(LeetCode)207. 课程表(C++)

拓扑排序 根据示例看出&#xff0c;课程表是否存在环&#xff0c;是问题的关键。这题的环&#xff0c;和数组、链表的环不一样&#xff0c;不好判&#xff0c;要转化成图判拓扑序列。 考虑向右和向左的方向&#xff0c;拓扑序列的所有边可以指向同一方向。 无环图进行重排序…

第一章:绪论

一、数据库系统概述 1、【单选题】记录内有结构&#xff0c;整体无结构&#xff0c;属于计算机发展过程的哪一阶段 正确答案&#xff1a; C 2、【单选题】数据库系统最小访问单位是 正确答案&#xff1a; C 3、【多选题】数据库管理系统提供的数据控制功能包括 正确答案&…

不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动

文章目录一、前言二、系列文章三、准备工作3.1 挑选I2C引脚3.2 测试设备&#xff1a;QMI8658C四、编写设备树节点4.1 查找MUX4.2 修改i2c1引脚配置4.2.1 修改前4.2.2 修改后五、编译、烧录dt.img5.1 烧录后效果六、编写test程序6.1 创建文件6.2 源码&#xff1a;Android.mk6.3 …

Docker常用操作命令总结(一)

文章目录一、Docker的应用场景二、Docker 的优点三、Docker 架构四、安装Docker1、更新 apt 包索引2、安装docker3、安装完成之后&#xff0c;运行命令sudo docker info&#xff0c;检查安装状态4、有可能&#xff0c;第一次需要手动启动服务.就需要执行下面的命令&#xff0c;…

图像处理:制作你的专属卡通头像和LOGO(圣诞节特别篇)

目录0 前言1 安装与贴图2 算法原理2.1 计算像素频率2.2 计算像素相对距离2.3 计算合适贴图3 配置功能4 使用&#xff1a;以圣诞老人为例0 前言 Tiler是一种使用各种其他较小图像平铺创建新图像的工具&#xff0c;它与其他马赛克工具不同&#xff0c;因为它可以适应多种形状、大…

基于Xlinx的时序分析与约束(5)----衍生时钟约束

衍生时钟约束语法 衍生时钟&#xff08;Generated Clocks&#xff0c;又称为生成时钟&#xff09;是指由设计中已有的主时钟通过倍频、分频或者相移等操作后产生的新的时钟信号&#xff0c;如由MMCM或PLL或由组合逻辑生成的倍、分频时钟信号。 衍生时钟约束必须指定时钟源&…