C语言--自定义数据类型(结构体、枚举、联合)


一、结构体

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

1.结构体声明

结构体的声明并不占用空间,只是提供一个图纸,定义变量时才会占用空间

struct tag
{
	member-list	成员列表
}variable-list;		变量列表

描述一个学生
struct Stu
{
	char name[20];
	char tele[12];
	char sex[10];
	int age;
}s4,s5,s6;			这三个为全局变量
				在函数内部声明为局部变量

typedef重命名

	typedef可以重新定义名字
typedef struct Stu
{
	char name[20]
	short age;
	char tale[12]
	char sex[5];
}Stu;			Stu是类型,与上面的s1,s2,s3全局变量不同
int main()
{
	struct Stu s1;
	Stu s1;
上面两个等价
}

匿名结构体类型 / 指针类型

匿名结构体类型
struct
{
	int a;
	char b;
}sa;		sa是结构体变量名字
匿名结构体指针类型
struct
{
	int a;
	char c;
}*psa;

2.结构体的自引用

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

typedef引发的自引用问题

正确的用法
typedef struct Node
{
	int date;
	struct Node* next;
}Node;
int main()
{
	struct Node n1;
	Node n2;
	return 0;
}
错误的用法
typedef struct
{
	int date
	Node* next;
}Node;
先有鸡现蛋的问题,会报错

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

  1. 可以在声明的同时定义变量,为全局变量
  2. 在函数内部定义结构体变量为局部变量
  3. 可以在定义的同时赋值,使用{ } 的格式
声明结构体类型
struct T
{
	double weight;
	short age;
};
声明结构体类型
struct S
{
	char c;
	struct T st;
	int a;
	double d;
	char arr[20];
};

int main()
{
	struct S s = { 'c' , { 55.6 , 30 }, 100 , 3.14 , "hello bit" };        初始化    结构体嵌套初始化
	printf("%c %d %lf %s\n",s.c,s.a,s.d,s.arr);            结构体成员访问
	printf("%lf\n",s.st.weight);                        结构体成员访问

	return 0;
}

4.结构体成员访问

  1. 通过 . 操作符访问
  2. 结构体指针,通过 -> 访问

5.结构体传参

传值调用

会创建一个新的空间,空间和时间都浪费严重

传址调用

传过去四个字节的大小,空间占用小,时间少速度快

总结

  1. 结构体传参的时候,要传结构体的地址
  2. 函数传参的时候,参数时需要压栈的,如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,会导致性能的下降

6.内存对齐

规则

  1. 第一个成员与结构体变量偏移量为0地址处
  2. 对齐数 = 编译器默认的对齐数 与 该成员大小的较小值
  3. 其它成员要对齐到某个数(对齐数)的整数倍的地址处
  4. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
  5. 如果嵌套了结构体,嵌套的结构体对齐到自己最大对齐数的整数倍
struct s3	16个字节最大对齐数8
{
	double d;
	char c;
	int i;
};

struct s4	32个字节最大对齐数8
{
	char c1;
	struct s3 s3;
	double d;
};

在这里插入图片描述

内存对齐的意义

结构体的内存对齐是拿空间换取时间的做法。
在设计结构体的时候,既要满足对齐,又要节省空间空间

平台原因

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

性能原因

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

修改默认对齐数

#pragma 预处理指令,可以改变默认对齐数
设置默认对齐数

#pragma pack(4)
struct S
{
	char c1;
	double d;
};
#pragma pack()    取消设置的默认对齐数

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

7.位段

C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。

规则

位段代表比特位
位段的声明和结构是类似的,有两个不同
1.位段成员必须是int,unsigned int,signed int
2.位段的成员名后有一个冒号和一个数字

struct A
{
	int _a:2;    2个比特位
	int _b:5;    5个比特位
	int _c:10;    10个比特位
	int _d:30;    30个比特位
	共47个比特位 6 个字节
};
int main()
{
	printf("%d",sizeof(strcut A));
	return 0; 
}
打印结果为8

位段的内存分配

  1. 位段的成员可以是int unsigned,int signed,int或者是char(属于整型家族)类型
  2. 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的,不足就新开辟
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
struct A
{
	int _a:2;    2个比特位
	int _b:5;    5个比特位
	int _c:10;    10个比特位
	int _d:30;    30个比特位
	共47个比特位 6 个字节
};
最后面的数字不能大于32
不够放就开辟一个新空间

在这里插入图片描述

位段的跨平台问题

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

位段的应用

在这里插入图片描述

二、枚举

把可能的取值一一列举
一周7天,性别男女,一年12个月份

1.枚举定义

定义枚举类型
enum Sex
{
枚举的可能取值
	MALE = 2,		MALE,			MALE = 2,
	FEMALE,			FEMALE = 8,		FEMALE,
	SECRET = 8,		SECRET			SECRET.
};
enum Color
{
	RED,			0
	GREEN,		1
	BLUE		2
};

int main()
{
	定义枚举类型变量
	enum Sex s = MALE;
	enum Color c = BLUE;
	enum Color c = 2;	这是int类型,会报错 
	整数不能直接赋给一个枚举变量,必须强制类型转换为枚举变量才行(enum day)
	printf("%d %d %d\n",RED,GREEN,BLUE);
打印结果为 0 1 2
	printf("%d %d %d\n",MALE,FEMALE,SECRET);
打印结果为 		  2 3 8
 		 		  0 8 9
 				  2 3 4
return 0;
}

2.优点

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

3.在内存中的存储

sizeof(枚举变量)得到的结果为一个整型int

enum Color
{
	RED,
	GREEN,
	BLUE
};
int main()
{
	enum Color c = RED;
	为整型类型,4个字节大小
	printf("%d\n", sizeof(c));
	打印结果为4
		return 0;
}

三、联合

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

1.联合定义

union In
{
	char c;
	int i;
};
int main()
{
	union Un n;
	printf("%d\n", szieof(u));
	printf("%p\n", &u);
	printf("%p\n", &(u.c));
	printf("%p\n", &(u.i));
	打印的地址都相同
		公用同一块空间
	return 0;
}

2.特点

  1. 联合的成员是公用一块内存空间的,这样一个联合变量占用的内存空间 等于它最大成员 所占的内存空间
  2. 在同一时刻只能使用一个成员,不能同时使用
  3. 不能对联合变量名赋值,也不能企图引用联合变量名来得到一个值。
  4. 联合变量的地址和其各个成员的地址是同一个地址。
  5. 不能用联合变量作为函数的参数或者是返回值,当需要在函数间传递联合变量时,可以使用指向联合变量的指针来实现!

3.内存中的存储

  1. 联合大小至少是最大成员的大小
  2. 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
union Un
{
	int a; 4默认8对齐数4
		char arr[5]; 1默认8对齐数1
		char arr[5]5个字节,4是最大对齐数
		5不是最大对齐数4的倍数
		浪费3个字节,凑到8个字节
};
int main()
{
	union Un u;
	printf("%d\n", sizeof(u));
	打印结果为8
		return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值