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

news2024/12/22 20:31:52

文章目录

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

在这里插入图片描述

结构体

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

结构体的声名

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个字节 - 32bit
	int _a : 2;//_a 成员占2个bit位
	int _b : 5;//_b 成员占5个bit位
	int _c : 10;//_c 成员占10个bit位
	//15
	//4个字节 - 32bit
	int _d : 30;//_d 成员占30个bit位
};
int main()
{
	printf("%d\n", sizeof(struct A));//8
	return 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.coloradmin.cn/o/115150.html

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

相关文章

【Linux】用户与用户组操作_补

文章目录一.用户1.1 用户与用户组概念1.2 与用户管理相关的系统文件1.3 查看用户组1.3.1用户组密码配置文件&#xff0f;etc&#xff0f;gshadow1.4用户管理创建用户修改用户添加密码一.用户 1.1 用户与用户组概念 用户和用户组的对应关系有&#xff1a;一对一、一对多、多对一…

【C语言进阶】指针练习题

写在前面 这是指有关指针的小题 正文 练习一 int main() {int a[5][5];int (*p)[4];pa;printf("%p,%d", &p[4][2]-&a[4][2], &p[4][2]-&a[4][2] );return 0; } 解析&#xff1a; a[4][2]为如图粉色部分&#xff0c;p[4][2]为如图蓝色部分。a的…

【ROS通信机制实战练习】通过话题发布实现turtlesim小乌龟圆周运动

本节记录下使用ROS中的话题机制&#xff0c;实现turtlesim中小乌龟的圆周运动。 如果想通过话题通信机制&#xff0c;实现小乌龟的圆周运动&#xff0c;需要首先明确小乌龟的运动情况&#xff0c;以及所涉及的指挥运动的参数&#xff0c;这里需要首先手动发布一个turtlesim的节…

springboot整合mybatis代码快速生成

特别说明&#xff1a;本次项目整合基于idea进行的&#xff0c;如果使用Eclipse可能操作会略有不同&#xff0c;不过总的来说不影响。 springboot整合之如何选择版本及项目搭建 springboot整合之版本号统一管理 springboot整合mybatis-plusdurid数据库连接池 springboot整合…

String 字符串

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

VUE3-计算属性和监听器《五》

目录 一&#xff0c;计算属性 二&#xff0c;监听器 在vue3种&#xff0c;当界面上需要处理一些数据的时候&#xff0c;可以通过计算属性和监听器处理&#xff0c;他们都是对一个属性进行操作的&#xff0c;然后返回数据。 他们的区别是&#xff0c;计算属性&#xff0c;是通…

20221226英语学习

今日短文 When we are shown two options, our eyes tend to flick from one to the other and back again several times as we deliberate on the pros and cons of each. Researchers at Johns Hopkins University in the US have found that the speed with which our ey…

Hbase是什么?

Hadoop Database简介表结构数据只能读不能改>生成新版本总结简介 永远的百度百科 HBase是一个分布式的、面向列的开源数据库&#xff0c;一个结构化数据的分布式存储系统”。 HBase不同于一般的关系数据库&#xff0c;它是一个适合于非结构化数据存储的数据库。另一个不同的…

过年首秀 - 用python写一个自动生成春联的软件并打包exe

前言 哈喽啊&#xff0c;我亲爱的铁铁们&#xff0c;I am back &#xff01;&#xff01; 别管&#xff0c;我也是阳过的人了&#xff0c;这么久都没有更新&#xff0c;今天就带来个小玩意吧 这不是过完圣诞就要过年了吗 这不得准备准备&#xff0c;春节的表演&#xff1f;…

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

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

「 理财与风险控制|养老系列」你想象中的高端养老社区是什么样?

本文主要介绍为什么养老规划需要考虑养老社区的部分&#xff0c;当前市场上养老社区的各种现状&#xff0c;养老社区从各个角度分类&#xff0c;选择养老社区需要关注的要素以及保险保单能够提供的养老权益是怎样的 文章目录01 为什么要关注养老社区&#xff1f;02 为什么关注高…

QT多窗口编程与文件IO编程

目录 一、消息对话框 QMessageBox&#xff08;掌握&#xff09; 二、常用窗口类&#xff08;掌握&#xff09; 三、主窗口类 QMainWindow&#xff08;重点&#xff09; 四、parent参数&#xff08;掌握&#xff09; 五、窗口传参 5.1 成员函数/构造函数 5.2 信号槽传参 六、事件…

劳动自由——你真的理解马克思说的劳动吗

目录 一、空谈“劳动是一切财富和一切文化的源泉”是错误的 个人理解——剥削与马斯洛需求的满足程度 二、马克思对劳动的定义是什么 三、马克思所说的劳动自由 1、实现劳动自由的实现路径 2、劳动自由的状态 一、空谈“劳动是一切财富和一切文化的源泉”是错误的 空谈所…

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

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

从华科到清华这些年,我和焦虑成为朋友

Datawhale干货 作者&#xff1a;赵子一&#xff0c;清华大学&#xff0c;Datawhale成员硕士求职碰壁被今年的求职形势狠狠地“教育”了一番2021年的秋天开启了我读硕士的第三个年头。当时的我还在某家公司做实习生。本来是可以转正的&#xff0c;但是出于各种考虑&#xff0c;我…

C++:STL:常用容器(上):deque容器

1&#xff1a;deque容器的基本概念 功能&#xff1a; 双端数组&#xff0c;可以对头端进行插入和删除操作。 deque 与vector区别 1&#xff1a;vector对于头部的插入删除效率低&#xff0c;数据量越大&#xff0c;效率越低。 2&#xff1a;deque相对而言&#xff0c;对头部的插…

产品第一性原理提升数据转化

小飞象交流会答应自己的事就尽力去做到&#xff0c;要去的地方就努力去抵达。内部交流│18期产品第一性原理提升数据转化data analysis●●●●分享人&#xff1a;永波&#xff08;阿外&#xff09;‍数据分析需要透过现象看本质&#xff0c;先从纷繁复杂的表象中进行抽丝剥茧&…

【HTML】2023跨年烟花代码

*2022年圣诞节到来啦&#xff0c;很高兴这次我们又能一起度过~ &#x1f4c2;文章目录前言效果展示一、夜景烟花绽放动画效果HTML源码2023年(新年)春节倒计时代码源码2023除夕倒计时效果展示源码宇宙星空-效果展示1.源码2.思路3.步骤(js部分)更多干货&#x1f680;前言 时光荏苒…

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

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

微信开放平台之第三方平台开发,模板小程序如何提交?

大家好&#xff0c;我是悟空码字 12月25日&#xff0c;天气晴朗&#xff0c;阳光普照&#xff0c;今天是圣诞节。因为疫情影响&#xff0c;小羊人的增多&#xff0c;街上放眼望去&#xff0c;人烟稀少。楼下除了几个十一二岁的小男孩在玩耍&#xff0c;也没有像往日老人悠闲打…