一文带你玩转自定义类型

news2024/10/6 18:31:51

 作者主页:paper jie的博客_CSDN博客

本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。

本文录入于《系统解析C语言》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将算法基础知识一网打尽,希望可以帮到读者们哦。

其他专栏:《算法详解》《C语言》《C语言-语法篇》等

内容分享:本期将对c语言中的自定义类型进行详细的讲解,各位看官姥爷快搬好小板凳坐好叭。

    -------- 不要998,不要98,只要一键三连,三连买不了吃亏,买不了上当

目录

结构体

结构体的声明

特殊的声明

结构体的自引用

结构体的定义和初始化

结构体内存对齐 

练习

内存对齐的原因

修改默认对齐数

 结构体传参

位段

位段的含义

位段的内存分配

位段的跨平台问题

位段的应用

枚举

枚举类型的定义

枚举的优点

枚举的使用

联合体

联合体的定义

联合的特点

联合体大小的计算


结构体

结构体是一些值的集合,这些值是它的成员。它们可以是不同类型的变量。

结构体的声明

注意:它的分号不能丢了

//栗子:这里定义一个学生
struct Stu
{
	int age;
	char name[20];
	char sex[5];
};

特殊的声明

在声明结构体的时候,还有一种特殊的声明,就是匿名结构体类型:

//栗子:
struct
{
	int a;
	char b;
	float c;
}s1;

struct
{
	int a;
	char b;
	float c;
}*p;

上面两个结构体省略了结构体的名字,这时有一个问题就是:

p = &s1在编译器上是编译不过去的。原因就是编译器把他们两个当成了不同的结构体类型。

结构体的自引用

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

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

结构体的定义和初始化

声明类型的时候可以定义变量,也可以在声明完后定义。初始化可以在定义完后初始化,也可以定义的同时初始化。

struct point
{
	int a;
	int b;
}s1;   //声明的同时定义
struct point s2; //先声明再定义结构体变量
//初始化:定义的同时初始化
struct point s3 = { 1,2 };


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

struct Stu s = { 20,"zhangsan" };

struct Node
{
	int date;
	struct Stu s1;
	struct Node* next;
}n1 = {1, {19, "lishi"}, NULL}; //结构体嵌套初始化

struct Node n2 = { 2, {25,"wangwu"}, NULL };//结构体嵌套初始化

结构体内存对齐 

对于结构体内存对齐有以下几点规则:

第一个成员放在与结构体变量偏移量为0的位位置

其他变量要对齐到对齐数的整数倍处的地址处

对齐数是编译器默认的一个对齐数与该成员大小的较小值(vs默认值为8,gcc,Linux没有默认对齐数,对齐数是成员自身大小)

结构体总大小必须是最大对齐数的倍数

如果是嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数的整数倍(含嵌套结构体的对齐数)

练习

//练习一
struct s1
{
	char c1;
	int i;
	char c2;
};

int main()
{
	printf("%d\n", sizeof(struct s1));
}

c1为首成员,对齐偏移量为0的位置,占1个字节。i为4个字节小于8,对齐数为4,从&c1+3的位置开始存放i。c2为1个字节小于8,对齐数为1,接在i后面存放。又因为它们加起来为9不是最大对齐数的倍数,所以要提升为12.

//练习二
struct s2
{
	char c1;
	char c2;
	int i;
};

int main()
{
	printf("%d\n", sizeof(struct s2));
}

 

//练习3
struct S3
{
	double d;
	char c;
	int i;
};

int main()
{
	printf("%d\n", sizeof(struct S3));
}

//练习4
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

int main()
{
	printf("%d\n", sizeof(struct S4));
}

 

内存对齐的原因

平台原因:

不是所有的硬件平台都能访问任意地址上的数据。某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

性能原因:

数据结构应该尽量的在自然边界上对齐。原因就是为了访问未对齐的内存,处理器需要进行两次内存访问。而对齐的内存只需要访问一次。

总得来说就是内存对齐是用来拿空间换时间的做法。

所以在设计结构体的时候我们要尽量的满足对齐,又节约空间。做法就是让空间小的成员尽量在一起:

//错误的做法
struct S1
{
	char c1;
	int i;
	char c2;
};
	 
//正确的做法
struct S2
{
	char c1;
	char c2;
	int i;
};

修改默认对齐数

修改对齐数我们需要用到一个预处理指令:#pragma,它可以改变我们的默认对齐数:

#pragma pack(8)
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()

#pragma pack(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;
}

 在我们遇到对齐方式不合适的时候,我们就可以用pragma来改变默认对齐数。

 结构体传参

结构体传参有两种方法:一种是直接传参,一种是地址传参。一般来说是地址传参比较好。

因为:函数传参需要压栈,会有时间和空间的开销。如果传递的结构体对象过大,那么参数压栈的开销就会比较大,所以会导致性能的下降。

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

//结构体传参
void print_f(struct Stu s)
{
	printf("%s\n", s.name);
}

//结构体地址传参
void print_t(struct Stu* s)
{
	printf("%d\n", s->age);
}

int main()
{
	print_f(s);//传结构体
	print_t(&s);//传地址
	return 0;
}

位段

位段的含义

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

段位的成员必须是int,unsigned int, signed int

段位的成员名后面有一个冒号和一个数字

举个栗子:

struct S
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};

位段的内存分配

段位的成员是int unsigned int signed int 或者是char类型

段位的空间是按照需要以4个字节或者1个字节的方式开辟的。

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

栗子:

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;
}

位段的跨平台问题

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

位段的应用

它可以极大的减少数据的空间,是数据可以高速传递

枚举

 枚举就是可能的情况一一列举。比如:生活中的星期,月份,性别都可以一一列举

枚举类型的定义

enum day
{
	mon,
	tues,
	wed,
	thur,
	fri,
	sat,
	sun,
};

enum color
{
	red,
	green,
	blue
};

以上定义的day和color都是枚举类型,{}中的内容是枚举类型的可能取值,叫枚举常量。这些值都是有默认值的,默认从0开始,每次增加1,在定义的时候也是可以赋值的:

enum day
{
	mon = 1,
	sun = 7,
	sat = 6
};

枚举的优点

增加代码的可读性和可维护性

和#define的标识符比较枚举有类型检查,更加严谨

防止了命名污染(封装)

便于调试

使用方便,一次可定义多个常量

枚举的使用

int main()
{
	enum color str = red; //只有拿枚举类型给枚举常量赋值,才不会出现类型的差异
	str = 6;
	return 0;
}

联合体

联合体的定义

联合体是一种特殊的自定义类型。这种类型的变量包括了许多的成员,他的特点是这些成员共同使用一块空间。

举个栗子:

union un
{
	char c;
	int i;
};
//联合变量的定义
union un u;

联合的特点

联合体的成员公用一块空间,这样的联合体大小至少是最大成员的大小。

通过下面代码可以发现,它们公用一块空间,且还会改变对方的值

union un
{
	int i;
	char c;
};

int main()
{
	union un u;

	printf("%p\n", &(u.i));
	printf("%p\n", &(u.c));

	u.i = 0x11223344;
	u.c = 0x55;
	printf("%x\n", u.i);
	return 0;
}

联合体大小的计算

联合的大小至少是最大成员的大小

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

栗子:

union un1
{
	char c[5];
	int i;
};

union un2
{
	short c[7];
	int i;
};

int main()
{
	printf("%d\n", sizeof(union un1));
	printf("%d\n", sizeof(union un2));
	return 0;
}


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

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

相关文章

Exception 类的层次

所有的异常类是从 java.lang.Exception 类继承的子类。 Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。 Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。 Error 用来指示运…

4027: 网络覆盖

4027: 网络覆盖 题目内容 有 n n n 个基站,他们可以抽象成一条数轴上的 n n n 个点,其中第 i i i 个基站在数轴上 x i x_i xi​ 的位置。 现在给每个基站分配一个半径 r i r_i ri​,这样对于第 i i i 个基站,它就会用信号…

uniapp中refs获取打印是空对象{}的解决办法

场景复现 版本如下: "dcloudio/uni-app": "2.0.2-3080720230703001", "vue": "> 2.6.14 < 2.7", 开发中发现只要是view这些原始标签的ref都无法在任何地方获取到refs.xxx, 而组件标签如<myStep></myStep> 这种加ref…

魔百盒cm101s m8233 emmc 卡刷教程

1、下载适用于对应型号的电视盒子刷机&#xff1b; https://download.csdn.net/download/qq_25601345/88051654?spm1001.2014.3001.5501 2、将下载好的四个文件放入u盘&#xff08;FAT32格式、最好4/8G的u盘&#xff09; 3、将u盘插入机顶盒的靠近网口的Usb接口 4、用镊子短…

python头部信息、py头部信息、python头信息、py头信息、py文件头部

文章目录 可指定以下信息1. 文件编码&#xff1a;# -*- coding: utf-8 -*-&#xff08;指定文件的字符编码&#xff0c;通常为UTF-8。&#xff09;2. 文件说明&#xff1a;Author等 对文件的简要说明&#xff0c;可以包括作者、创建日期、修改日期等信息。3. 版本信息&#xff…

观察一个StaticMesh加载其对应DDC文件的流程

无用的前言 很久前我观察过DDC的一些代码了解了些浅显的知识。 最近我遇到个DDC相关的问题&#xff0c;于是将之前写的东西又复习了一遍。同时我也将记录下我最近研究这个问题时&#xff0c;一些重要的部分以作备忘。 目标 观察一个StaticMesh加载其对应DDC文件的流程&#…

Python 列表 reverse()函数使用详解

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;小白零基础《Python入门到精通》 reverse函数使用详解 1、reverse()会修改原始副本2、reverse()和reversed()的区别…

小白学懂分频器(二)

分频&#xff1a;简单来说&#xff0c;二分频后的方波一个周期为标准方波高低电平循环两个周期&#xff0c;四分频为4个周期。分频后的时钟周期为原来的n倍&#xff0c;即为n分频。   频率和周期的关系&#xff1a;f1/T &#xff08;1&#xff09;简单的计数器 计数器实质是…

【数据结构】_1.集合与复杂度

目录 1. 集合框架 2. 时间复杂度 2.1 时间复杂度和空间复杂度 2.2 时间复杂度的概念 2.3 大O的渐进表示法 2.3.1 精确的时间复杂度表达式 2.3.2 大O渐进表示法的三条规则 2.3.3 时间复杂度的最好、平均与最坏情况 2.4 时间复杂度计算示例 3.空间复杂度 1. 集合框架 …

【多线程例题】编写代码, 实现多线程数组求和.【本题学习关键点:通过一个类 进行加法】

【多线程例题】编写代码, 实现多线程数组求和. import java.util.Random;/*** 题目名称 :* 编写代码, 实现多线程数组求和.* 题目内容 :* 1. 给定一个很长的数组 (长度 1000w), 通过随机数的方式生成 1-100 之间的整数.* 2. 实现代码, 能够创建两个线程, 对这个数组的所有元素求…

CORTEX-M系列处理器

1.ARM处理器的发展 随着智能终端、人工智能、5G、物联网等技术的快速发展&#xff0c;半导体行业蓬勃发展了数十年。处理器芯片作为这些技术的“心脏”&#xff0c;迎来了井喷式的增长。世界主流的处理器分为4大类&#xff1a;Interl公司和AMD公司的的X86架构处理器&#xff0…

利用集合框架实现-超市会员管理系统

借助集合框架来实现超市会员管理系统&#xff0c;实现以下功能&#xff1a; 1.开卡 2.积分累计 3.查询剩余积分 4.积分兑换 5.修改密码 6.退出 -------------------------------------------------------------------------------------------------- 展示&#x…

第一堂棒球课:MLB棒球大联盟青训体系·野球1号位

介绍MLB棒球大联盟青训体系 1. 引言 这里我们将深入探讨MLB棒球大联盟青训体系的故事&#xff0c;了解它是如何成为全球青少年最梦寐以求的梦想&#xff0c;以及它对世界棒球运动产生的深远影响。 MLB棒球大联盟青训体系&#xff0c;即MLB Youth Coaching Program&#xff0c…

java项目之东理咨询交流论坛(ssm+mysql+jsp)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的东理咨询交流论坛。技术交流和部署相关看文章末尾&#xff01; 开发环境&#xff1a; 后端&#xff1a; 开发语言&#xff1a;Java 框架…

Java并发编程(10) —— ReentrantLock类详解

一、ReentrantLock介绍 ReentrantLock是juc.locks包中的一个独占式可重入锁&#xff0c;相比synchronized&#xff0c;它可以创建多个条件等待队列&#xff0c;还支持公平/非公平锁、可中断、超时、轮询等特性。 ReentrantLock实现Lock接口实现了一个锁所需的方法&#xff0c…

CSS基础特性

一、CSS概述 1.1、概述 CSS&#xff08;层叠样式表&#xff09;是一种用来表现HTML&#xff08;标准通用标记语言的一个应用&#xff09;或XML&#xff08;标准通用标记语言的一个子集&#xff09;等文件样式的计算机语言。CSS 不仅可以静态地修饰网页&#xff0c;还可以配合…

[JVM] 3. 类加载子系统(2)-- 类加载器、双亲委派机制(JDK1.8及之前)及其他

前言 JDK1.8及之前和JDK9及之后的双亲委派模型是不一样的&#xff0c;这里学习了1.8及以前的双亲委派模型&#xff0c;记录笔记 一、类加载器 1.8之前主要是这几种类加载器&#xff1a; 1. 启动类加载器(Bootstrap ClassLoader)&#xff1a; 负责将存放在<JAVA_HOME>…

JavaScript中值和变量

值是指可以存储在计算机内存中的数据。这些数据可以是数字、字符串、布尔值、对象等。变量是一种用于存储值的容器。 ● 在JS中&#xff0c;我们可以使用let参数也创建一个变量&#xff0c;使用给变量赋值&#xff0c;例如 let ITshare "IT知识一享";● 之后我们打…

力扣小技巧:如何用最简单的方式实现小写字母转换

本篇博客会讲解力扣“709. 转换成小写字母”的解题思路&#xff0c;这是题目链接。 本题的解法非常简单&#xff0c;只需利用tolower函数即可。这个函数的作用是将所有大写字母转换为小写字母&#xff0c;而对其他字符不做任何改变。 char * toLowerCase(char * s){// 把字符串…

1769_Source Insight数字前缀后缀乱码解决方法

全部学习汇总&#xff1a; GreyZhang/editors_skills: Summary for some common editor skills I used. (github.com) 这算是一个偶然的发现&#xff0c;写代码的时候发现十六进制的数字显示前缀0x显示错误。如果只输入一个0x正常&#xff0c;写成一个十六进制数据诸如0x123之后…