C语言——自定义类型

目录

一、结构体

1、什么是结构体

2、为什么要有结构体

3、结构体的声明

4、结构体的定义、初始化与访问

5、结构体内存对齐

 二、位段(位域)

1、什么是位段

2、位段的声明

3、位段的内存分配

4、位段的存储

5、位段的跨平台问题

 三、枚举

1、枚举的声明

2、枚举的优点

4、 联合体(共用体)

1、联合类型的定义

2、联合的特点

3、联合大小的计算


一、结构体

1、什么是结构体

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

2、为什么要有结构体

在实际的过程中,一个对象往往是有不同的属性来描述的,例如学生这个对象,包括姓名,年龄,学号,性别,成绩等等不同的属性。因此我们往往还需要一组类型不同的数据来描述这个对象,因为数据类型不同,显然不能用一个数组来存放,而是用自定义类型结构体来描述。

3、结构体的声明

struct tag    //struct-结构体关键字  tag-结构体标签     struct tag-结构体类型
{
	member-list;    //成员列表
}variable-list;  //variable-list 变量列表名 

 3.1、变量列表名可以省略。

//结构体声明:声明一个结构体类型;用于描述学生  
struct stu
{
	//成员变量 
	char name[20];    //学生姓名
	short age;    //学生年龄
	char tale[12];    //学生电话
	char sex[5];    //学生性别
};  
//上述这种写法表明仅定义一个结构体类型,不占据空间 

3.2、不省略变量列表名。

//结构体声明:声明一个结构体类型;用于描述学生  
struct stu
{
	//成员变量 
	char name[20];    //学生姓名
	short age;    //学生年龄
	char tale[12];    //学生电话
	char sex[5];    //学生性别
}s1,s2,s3;  
//上述这种写法不仅定义了一个结构体类型,还创建了 s1,s2,s3 三个struct stu 类型的全局变量。

3.3、特殊的声明——匿名结构体

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

//匿名结构体类型——这种类型结构体存在唯一性
struct
{
	int a;
	char b;
	float c;
}x;//需要直接创建结构体变量

4、结构体的定义、初始化与访问

 访问结构体成员有 ' . ' 和 ' -> '两种方式。其中 ' . '用于结构体变量直接访问成员,而 ' -> '用于结构体指针访问成员。

#include<stdio.h>
#include<string.h>
struct stu
{
	//成员变量 
	char name[20];    //学生姓名
	short age;    //学生年龄
	char tale[12];    //学生电话
	char sex[5];    //学生性别
}s1;

void print(struct stu* s1)
{
	printf("%s %d %s %s\n", s1->name, s1->age, s1->tale, s1->sex);
}

void print2(struct stu s2)
{
	printf("%s %d %s %s\n", s2.name,s2.age,s2.tale,s2.sex);
}
int main()
{

    //先定义后初始化
	strcpy(s1.name, "李四");
	s1.age = 20;
	strcpy(s1.tale, "222222222222");
	strcpy(s1.sex, "男");
    //结构体指针访问成员
	print(&s1);    

    //定义的同时进行初始化
	struct stu s2 = { "张三",20,"111111111111","男" };
	//结构体变量访问成员
	print2(s2);

	return 0;
}

5、结构体内存对齐

5.1、什么是内存对齐

结构体成员是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。从结构体存储的首地址开始,成员放置的位置一定会在自身大小的整数倍上开始,这就是所谓的内存对齐。

5.2、为什么存在内存对齐

大部分的参考资料都是如是说的:

1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能 在某些地址处取某些特定类型的数据,否则抛出硬件异常。

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

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

 5.3、对齐规则

1. 第一个成员在结构体变量偏移量为0的地址处。

2. 其他成员变量要对齐到 对齐数 的整数倍的地址处。

对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 

3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

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

 下面我们来看一下具体的计算:

struct S1
{
	char c1;
	int i;
	char c2;
};//大小为 12 字节

 二、位段(位域)

1、什么是位段

信息的存取一般以字节为单位。实际上,有时存储一个信息不必用一个或多个字节,为了节省空间,常常在一个字节中放几个信息。C语言允许在一个 结构体 中以位为单位来指定其成员所占 内存 长度。

2、位段的声明

(1)位段的宽度不能超过它所依附的数据类型的长度。

(2)C语言标准还规定,只有有限的几种数据类型可以用于,位段的成员必须是 int、unsigned int 或signed int 。

(3)但编译器在具体实现时都进行了扩展,额外支持了其他类型。

struct B
{
	char _a : 2;    //数字表示占据的位数
	char _b : 4;
	char _c : 6;
};

3、位段的内存分配

(1)位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的(2)位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

4、位段的存储

 C语言标准并没有规定位域的具体存储方式,不同的编译器有不同的实现,但它们都尽量压缩存储空间。
    1) 当相邻成员的类型相同时,如果它们的位宽之和小于类型的 sizeof 大小,那么后面的成员紧邻前一个成员存储,直到不能容纳为止;
       如果它们的位宽之和大于类型的 sizeof 大小,那么后面的成员将从新的存储单元开始,其偏移量为类型大小的整数倍。
    2) 当相邻成员的类型不同时,不同的编译器有不同的实现方案
    3) 如果成员之间穿插着非位域成员,那么一般不会进行压缩。

struct A //A就是一个位段类型。
{
	int _a; //_a 没有限制占用位数,则根据数据类型即可推算出它占用 4 个字节(Byte)的内存
	//后面的数字用来限定成员变量占用的位数
	int _b : 4;//占用 4 位的空间
	int _c : 10;//占用 10 位的空间
};
struct B
{
	char _a : 2;
	char _b : 4;
	char _c : 6;
};
struct C
{
	//如果成员之间穿插着非位域成员,那么一般不会进行压缩。
	int _a : 2;
	int _b;
	int _c : 6;
};
struct D
{
	//当相邻成员的类型不同时,不同的编译器有不同的实现方案
	char _a : 2;
	int  _b : 4;
	char _c : 6;
};
int main()
{
	printf("%d\n", sizeof(struct A));	//大小为 8 字节
	printf("%d\n", sizeof(struct B));	//大小为 2 字节
	printf("%d\n", sizeof(struct C));	//大小为 12 字节
	printf("%d\n", sizeof(struct D));	//大小为 12 字节
	return 0;
}

5、位段的跨平台问题

(1). int 位段被当成有符号数还是无符号数是不确定的。
(2). 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。

(3). 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

(4). 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

 三、枚举

1、枚举的声明

注意事项:

(1)枚举常量, 以  ' , ' 分开,最后一项列表后面不用符号

(2)每一项常量都有默认值,每项都比前一项增加 1,默认从 0 开始

(3)枚举常量都是全局常量

(4)它们不占用数据区(常量区、全局数据区、栈区和堆区)的内存,而是直接被编译到命令(转换成了默认值)里面,放到代码区,所以不能用 & 取得它们的地址。

enum typename //enum是枚举关键字 ; enum typename是枚举类型
{
	valuenamelist1,
	valuenamelist2,
	valuenamelist3
}variablelist;   创建枚举类型的全局变量(可以不创建) 不能省略 ';'
enum color
{
	RED,//默认值为 0
	GREEN,//默认值为 1
	BLUE//默认值为 2
};

enum week
{
	//可以自己 设置 默认值
	MON = 1,	//设置默认值 = 1
	TUES,		//默认值为 2
	WED,		//默认值为 3
	THURS = 256,	//设置默认值为 256
	FRU,			//默认值为 257
	SAT,			//默认值为 258
	SUN				//默认值为 259
};

int main()
{
	printf("RED=%d GREEN=%d BLUE=%d\n", RED, GREEN, BLUE);
	printf("MON=%d TUES=%d WED=%d THURS=%d FRU=%d SAT=%d SUN=%d\n", MON, TUES, WED, THURS, FRU, SAT, SUN);

	//枚举类型变量需要存放的是一个整数,长度和 int 应该相同
	printf("%d %d %d %d\n", sizeof(RED), sizeof(GREEN), sizeof(BLUE), sizeof(enum color));

	return 0;
}

2、枚举的优点

我们可以使用 #define 定义常量,为什么非要使用枚举?
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量
6.#define命令虽然能解决问题,但也带来了不小的副作用,导致宏名过多,代码松散

4、 联合体(共用体)

1、联合类型的定义

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

2、联合的特点

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

3、联合大小的计算

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

union un//联合体的声明
{
	char c;
	int i;
};

union un s;//联合体的定义
int main()
{
	s.i = 0x11223344;
	s.c = 0x55;

	printf("%#x %#x\n\n",s.c,s.i);
	printf("%d\n", sizeof(s));

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值