【C语言】自定义类型:结构体【结构体内存具详细】,枚举,联合

news2025/1/11 12:54:58

 

目录

一、结构体

1.结构的声明

 2.特殊的声明

3.结构的自引用

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

 5.结构体内存对齐(重点来了)

6.为什么会存在内存对齐 

7.修改默认对齐数

8.结构体传参

二、位段

1.什么是位段

2.位段的内存分配

3.位段的跨平台问题

三、枚举

1.枚举类型的定义

2.枚举的优点

3.枚举的使用

四、联合(共用体)


一、结构体

在此之前简单地介绍了结构体的使用初阶结构体

首先 结构结构是一些值得集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

1.结构的声明

//结构体的声明
struct tag 
{
	member - list;//成员列表
}variable-list;//变量列表

【例如】

struct Stu
{
	char name[20];
	int age;
	char sex[5];
	char id[20];
};

 2.特殊的声明

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

如 匿名结构体类型

//匿名结构体类型
struct 
{
	int a;
	char b;
	float c;
}x;
struct 
{
	int a;
	char b;
	float c;
}a[20],*p;
//注意 不能这样写 p = &x;//编译器会把上述当成两个不同的结构体,所以是非法的

3.结构的自引用

就是结构体中含有一个类型为结构体本身的成员,如果学过数据结构的话,可能会知道链表中的node节点 就用到了结构体的自引用。

结构体的自引用方式

struct Node 
{
	int data;//数据域
	struct Node* next;//指针域,指向下一个节点
};
//另一种写法,使用类型重命名
typedef struct Node
{
	int data;
	struct Node* next;
}Node;//这样就可以 当写 struct Node 可以写为 Node

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

(1)声明类型的同时定义变量p1

//结构体变量的定义和初始化
struct Point
{
	int x;
	int y;
}p1;//声明类型的同时定义变量p1

(2)初始化,定义变量的同时赋值

struct Point p3 = {x,y};

struct Stu 
{
	char name[15];
	int age;
};
struct Stu s = {"zhangsan",20};

(3)结构体嵌套初始化

struct Stu 
{
	int data;
	struct Point p;
	struct Node* next;
}n1 = { 10,{4,5},NULL };//结构体嵌套初始化

 5.结构体内存对齐(重点来了)

先来个问题,如何计算结构体的大小呢?这个问题就涉及了结构体内存对齐

看下方代码,并试着计算这俩个结构体分别是多大

struct S1 
{
	char c1;
	int a;
	char c2;
};
struct S2
{
	char c1;
	char c2;
	int a;
};

如果不了解结构体内存对齐的话,可能会认为这两个结构体不一样大吗?

当我们试着打印这两个结构体的大小

这时我们发现这两个结构体的大小不一样大,这是为什么呢?

看完接下来的结构体内存对齐,就明白了。

重点

要计算结构体的大小,那么我们就要掌握下方的

结构体对齐规则:

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

根据上述结构体对齐规则,我们来分析下方代码

【1】

struct S1 
{
	char c1;//所占大小为1
	int a;//所占大小为4
	char c2;//所占大小为1
};
//该结构体的总体大小为?

该图解具详细 

接着来分析   struct S2

【2】

struct S2
{
	char c1;//所占大小为1
	char c2;//所占大小为1
	int a;//所占大小为4
};

//该结构体总体大小为?

图解具详细:

有了上述两个 我们接着再来看两个,巩固一下结构体内存

【3】根据结构体的内存对齐计算结构体的大小

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

图解具详细:

【4】 计算结构体S4的大小

struct S3 
{
	double d;
	char c;
	int i;
};
struct S4 
{
	char z;
	struct S3 s3;
	double d;
};

6.为什么会存在内存对齐 

简单来说就是

  1. 平台原因 某些硬件导致的原因
  2. 性能原因 访问一些内存时,在访问为未对齐的内存,可能会访问两次内存,而访问内存对齐的内存时 ,只要访问一次即可 

所以,对于结构体的内存对齐 是 通过 空间 来换取 时间 的做法

7.修改默认对齐数

我们可能之前会看到#pragma 这个预处理指令

#pragma 可以修改默认对齐数

#include<stdio.h>
#pragma pack(1)//对齐数修改为4
struct S1 
{
	char c1;
	int a;
	char c2;
};
int main() 
{
	printf("%d",sizeof(struct S1));
}

这样经过修改默认对齐数,改为1。这样计算出来的结构体大小就是6了。

有了这个pragma,当结构体对齐方式不合适的时候我们就可以修改默认对齐数,进而来调整结构体的大小。

8.结构体传参

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

注意: 因为函数传参的时候要压栈,会有时间和空间上的系统开销。而传结构体的地址的时候与会减少系统的开销。

二、位段

1.什么是位段

位段的声明和结构体是类似的,但有两点不同

  1. 位段的成员必须是int、unsigned int 、
  2. 位段的成员名后边有一个冒号和一个数字。

【例】

struct A {
	int a : 2;
	int b : 3;
	int c : 4;
	int d : 5;
};

2.位段的内存分配

位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型

位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。

位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

3.位段的跨平台问题

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

三、枚举

1.枚举类型的定义

枚举就是列举

enum Color 
{
	RED,
	GREEN,
	BLUE
};
enum Sex 
{
	MALE,
	FEMALE,
	SECRET
};

enum Sex,enum Color都是枚举类型,{ }中的内容就是枚举类型可能取的值,也叫枚举常量

有些可以有值得,默认从0开始,一次递增1,定义的时候可以赋值

enum Color 
{
	RED=1,
	GREEN=2,
	BLUE=3
};

2.枚举的优点

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

3.枚举的使用

enum Color
{
	RED = 1,
	GREEN = 2,
	BLUE = 3
};
enum Color clr = GREEN;//这样不会出现类型的差异
clr = 5;

四、联合(共用体)

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

#include<stdio.h>
union Un
{
	int i;
	char c;
};
union Un un;
int main()
{
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%d\n",&(un.i));
	printf("%d\n",&(un.c));
	printf("%x\n",un.i);
	return 0;
}

从第三个的打印结果我们可以看出来 联合体中的成员就是共用同一块内存空间。

来一个笔试题

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

//判断大小端的存储
#include<stdio.h>
int check_sys() 
{
	int i = 1;
	return *((char*)&i);
	// 00 00 00 01 大端
	// 01 00 00 00 小端
	//地址 低---->高
}
int main() 
{
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");
	return 0;
}

上述代码就是我们通过取地址的方式来判断的

接下来我们来使用联合体来判断判断当前机器的字节序

//使用联合体来判断当前机器的字节序
#include<stdio.h>
int check_sys() 
{
	union 
	{
		int i;
		char c;
	}un;
	un.i = 1;
	return un.c;
	//这里的联合体共用是同一块内存空间
}
int main() 
{
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");
	return 0;
}

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

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

相关文章

各种存储性能瓶颈如何分析与优化?

【摘要】本文结合实践剖析存储系统的架构及运行原理&#xff0c;深入分析各种存储性能瓶颈场景&#xff0c;并提出相应的性能优化手段&#xff0c;希望对同行有一定的借鉴和参考价值。 【作者】陈萍春&#xff0c;现就职于保险行业&#xff0c;拥有多年的系统、存储以及数据备…

vue3+TS前端JS实现 搜索关键词变红

起初在网上搜索获得的处理方式大都是类似这种&#xff1a; 但是实际使用中发现&#xff0c;对于汉字和数字是没有问题的&#xff0c;但是如果有字母就会出现问题。 1.只有汉字和数字的时候&#xff1a;匹配正常。 2.当有字母的时候&#xff1a;异常替换。 原因&#xff1a;第二…

百度SEO优化技巧(选择、网站结构、内容优化、外链建设、数据分析)

百度关键词SEO优化介绍 SEO是搜索引擎优化的缩写&#xff0c;是指通过优化网站结构、内容和外部链接等方式&#xff0c;提高网站在搜索引擎中的排名&#xff0c;从而获取更多的访问量和流量。百度是中国最大的搜索引擎之一&#xff0c;对于企业来说&#xff0c;优化百度关键词…

千呼万唤openGauss资源池化系列培训来了

应openGauss广大用户要求&#xff0c;社区于近期推出openGauss资源池化培训系列。 关于资源池化 资源池化是openGauss 5.0.0 推出的重点特性&#xff0c;是openGauss基于内存池化和共享存储实现的数据库集群。数据在集群的计算节点内存、共享存储中实现共享。应用可以任意节点…

嵌入式学习攻略

嵌入式软件编程的基础 主要是学习编程语言、开发环境和形成自己的编程逻辑&#xff0c;为嵌入式软件开发打下良好的基础&#xff0c;编程语言建议为C和C语言。书籍中的例子都是比较经典的程序实例&#xff0c;尽量去搞懂&#xff0c;不要觉得太长或者太难了而放弃&#xff0c;…

[正确重装docker] Win10 重装 Docker 提示 Exising installation is up to date 的正确姿势

Win10 重装 Docker 报错 Exising installation is up to date 的一种情况是原来的 docker 没有卸载干净&#xff0c;或者说&#xff0c;没有正确卸载。 巧了&#xff0c;我就是直接删除了&#xff0c;因为一些原因重装了好几次&#xff0c;血泪史留给各位嘲笑。 一条正确的卸…

编程(48)----------网页打开的过程

一个网页的打开大致分为以下几步: 1.DNS查询 在进行网络访问过程中, 实际上所访问的是IP地址. 但输入的却仅仅是域名. 因为IP地址过于复杂不利于记忆. 因此, 需要将IP转换成更具有辨识度的域名. 通过输入域名, 以DNS进行转换为IP, 再发起请求. DNS在得到域名后会进行查询, …

P7075 [CSP-S2020] 儒略日(内附封面)

[CSP-S2020] 儒略日 题目描述 为了简便计算&#xff0c;天文学家们使用儒略日&#xff08;Julian day&#xff09;来表达时间。所谓儒略日&#xff0c;其定义为从公元前 4713 年 1 月 1 日正午 12 点到此后某一时刻间所经过的天数&#xff0c;不满一天者用小数表达。若利用这…

得帆信息副总裁——陈明:低代码企业内部推广场景建议

在之前的文章中有介绍过#企业内部如何做低代码的运营推广&#xff08;☜点击回顾精彩&#xff09;&#xff0c;感兴趣的可以结合之前的文章进行了解。 结合本人这几年的低代码落地推广经验&#xff0c;不论是由得帆主导的低代码平台运营推广&#xff0c;还是由客户自身主导进行…

广通优云完成2亿元C轮融资,加速平台级运维产品的技术创新

广通优云于近期完成2亿元C轮融资&#xff0c;松禾资本领投&#xff0c;朗玛峰创投、奇安投资、舜宇投资、天雅资本等机构跟投&#xff0c;皓石资本担任本轮独家财务顾问。资金将主要应用于公司产品创新研发、团队人才建设及市场生态开拓等方面。 在当前资本市场遇冷的背景下&a…

RSS阅读器

focus&#xff1a;免费、无广告、已停止维护[2020]、开源 也许后面我会维护更新。 Archive: https://www.ihewro.com/archives/948/ 开源代码&#xff1a;https://github.com/ihewro/Focus &#x1f370;借助RSShub开源项目&#xff0c;自带丰富订阅市场&#xff0c;尽可能简…

【大数据之Kafka】十四、Kafka-Eagle监控

Kafka-Eagle 框架可以监控Kafka 集群的整体运行情况&#xff0c;在生产环境中经常使用。 1 MySQL环境准备 https://blog.csdn.net/qq_18625571/article/details/130613704?spm1001.2014.3001.5501 2 Kafka环境准备 &#xff08;1&#xff09;关闭Kafka集群。 kf.sh stop&…

反向动力学Ik学习

参考文章&#xff1a;&#xff08;非本人原创&#xff09; 英文原文&#xff1a;Inverse Kinematics Techniques in Computer Graphics: A Survey (andreasaristidou.com) 知乎翻译文章&#xff1a; 【游戏开发】逆向运动学&#xff08;IK&#xff09;详解 - 知乎 (zhihu.co…

django创建web服务器

安装 pip install django 创建项目 django-admin startproject report django-admin startapp data //project下可创建多个app 执行使用 python manage.py migrate //orm代码到数据库 python manage.py runserver 0.0.0.0:80 权限管理 python manage.py createsuperuser 创建…

住宅区电力系统管理,这一点很重要!

随着现代社会对电力供应的不断增长需求&#xff0c;住宅小区的电力分配系统变得越来越复杂。电力是我们日常生活中不可或缺的一部分&#xff0c;因此确保小区的电力供应安全和稳定至关重要。 配电柜监控是确保电力系统安全和稳定运行的重要工具。它可以提高电力供应的可靠性&am…

掌动智能浅谈UI自动化测试工具的重要性

在现代软件开发中&#xff0c;用户界面(UI)的质量和可靠性对于一个应用的成功至关重要。为了确保应用在各种环境和设备上都能正常运行&#xff0c;开发团队需要进行全面的UI测试。为了提高测试效率和减少人为错误&#xff0c;UI自动化测试工具成为不可或缺的工具。本文将探讨UI…

Redis缓存实现及其常见问题解决方案

随着互联网技术的发展&#xff0c;数据处理的速度和效率成为了衡量一个系统性能的重要指标。在众多的数据处理技术中&#xff0c;缓存技术以其出色的性能优化效果&#xff0c;成为了不可或缺的一环。而在众多的缓存技术中&#xff0c;Redis 以其出色的性能和丰富的功能&#xf…

安卓系统--翻译手机rom语言 添加多国语言 编译apk 反编译ODEX 工具步骤解析

很多小品牌机型不具备多语言设置。国内大都是中文。要想换为其他语言除非固件支持。例如国际版固件等等。大厂基本都有中文或者英文或者其他语言配置。而小品牌机型只能通过修改rom来达到多语言调用. 工具步骤演示 今天给友友介绍一款工具&#xff0c;可以用来翻译手机rom语言…

Android Studio 打不开

现象&#xff1a;点击PC端上Android Studio 图标&#xff0c;没有反应&#xff0c;AS没有打开 环境&#xff1a;windows 11 解决办法&#xff1a; 1、找到任务管理器 2、发现后台进程中&#xff0c;有Android Studio 3、结束该任务。 4、再此点击Android Studio 图标&…

阿里系常用代码规范及工具

1. 前言 1.1 为什么需要代码规范 可能有同学觉得代码需要突出个人的特点&#xff0c;需要特异化&#xff0c;但是这样就会造成别人阅读起来很困难 代码不仅仅是交给机器来执行的&#xff0c;同时代码也是让其他同事来阅读的&#xff0c;如果代码不规范就会出现各种各样的问题&…