自定义类型:结构体、枚举、联合体

结构体

目录

结构体

结构的声明:

结构体的自引用(结构体的嵌套使用):

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

结构体嵌套初始化:

结构体内存对齐(比较重要):

 结构体传参:

位段

枚举 

联合体


  • 结构体类型的声明
  • 结构体的自引用
  • 结构体变量的定义和初始化
  • 结构体内存对齐
  • 结构体传参
  • 结构体实现位段(位段的填充&可移植性) 

结构的声明:

#include <stdio.h>
int main()
{
	//描述一个学生
	struct Stu
	{
		char name[20];
		int age;
		char sex[5];
		char id[20];
	};//分号很重要,不能丢
}

结构体的自引用(结构体的嵌套使用):

#include <stdio.h>
int main()
{
	//描述一个学生
	struct Stu
	{
		char name[20];
		int age;
		char sex[5];
		char id[20];
		struct Stu* next;//结构体的自引用
	};
}

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

#include <stdio.h>
int main()
{
	//描述一个学生
	struct Stu
	{
		char name[20];
		int age;
		char sex[5];
		char id[20];
	}Stu1;//声明类型的同时定义变量 Stu1
	struct Stu p2;//定义结构体变量 Stu2
	struct Stu Stu3 = { "lisi",666,"male","xatu" };//结构体成员的初始化:定义变量的同时赋初值
}

结构体嵌套初始化:

#include <stdio.h>
int main()
{
	struct Teacher
	{
		int age;
		char id[10];
	};
	//描述一个学生
	struct Stu
	{
		char name[20];
		int age;
		char sex[10];
		char id[20];
		struct Teacher W;
		struct Stu* next;//结构体的自引用
	};
	struct Stu Stu1 = { "wangwu",888,"male","xatu",{88,"xatu"} };//结构体的嵌套初始化
}

结构体内存对齐(比较重要):

首先了解一下为什么要有内存对齐:

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

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

就啥意思呢,就是说,结构体的内存对齐是拿空间来换时间的做法

首先记住结构内存对齐的四大原则:

  • 基本数据类型自身对齐值(下面已列出)。
  • 自定义类型的自身对其值:结构体或类的成员中自身对其最大的值。
  • 程序的指定对齐值:即#pragma pack(value)时的指定对齐值value,当结构对齐方式不合适的时候,我们可以自行更改默认的对齐数。
  • 程序的有效对齐值:自定义类型的自身对齐值和指定对齐值中较小的值。

牢记基本数据类型的自身对其值:

char类型:    1字节

short类型:   2字节

int 和 float:  4字节

double类型:8字节

 举个栗子:

#include <stdio.h>
int main()
{
	//不同的编译器都有一个默认的对齐数,在这里我用的是VS2019,它的默认对齐数是8,当然你也可以自己指定对齐数 #pragma pack(value),value 即为指定对齐值
	struct Teacher
	{
		int age;  // 4          int 型占4个字节
		char id;  //1  +  3     char 型占1个字节
	};//给char后 +3这样的话这个结构体总共占的字节数是 8 ,根据对齐原则,(1 + 3)是4的倍数,(1+3+4)= 8是8的倍数,此时达到内存对齐

 由上面举的这个栗子我们可以看到,在设计结构体的时候,如果我们让占用空间小的成员尽量集中在一起的话,是不是可以既满足对齐又可以节省空间,不明白的话没关系,再来一颗栗子:

#include <stdio.h>
int main()
{
	struct Teacher
	{
		double id; //8
		char sex;  //1 + 3
		int age;  // 4          int 型占4个字节
		char id;  //1      char 型占1个字节
	};//一共占了17个字节
}


#include <stdio.h>
int main()
{
	struct Teacher
	{
		char id;   //1
		char sex;  //1 + 2   
		int age;   //4
		double id; //8
	};//一共占了 16 个字节
}

我们可以看到,在成员都一样的前提下,下边的结构体的成员排序方式时比上边的更节省空间的 。

 结构体传参:

#include <stdio.h>
void print1();
void print2();
struct Stu
{
	int age;
	char id[20];
};
struct Stu stu1 = { 19 ,"54188" };
int main()
{
	print1(stu1);
	print2(&stu1);
	return 0;
}
void print1(struct Stu stu1)
{
	printf("%d\n", stu1.age);//结构体传参
}
void print2(struct Stu *stu1)
{
	printf("%d\n", stu1->age);//结构体地址传参
}

上面两种结构体传参方式都是可以的,但我们更推荐下边以传递结构体地址进行传参的方式,因为上边这种传参方式,因为函数传参时,参数是需要压栈的,有时间和空间上的系统开销。

 位段

位段的声明和结构是类似的:

  • 位段的成员必须是 int ,unsigned int ,或 sigend int。
  • 位段的成员名后边有一个冒号和一个数字。 

举个栗子:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//Stu即为一个位段类型
	struct Stu
	{
		int a : 2;
		int b : 5;
		int c : 10;
		int d : 30;
	};//Stu的大小为 8 个字节
	printf("%d\n", sizeof(struct Stu));
	system("pause");
}

位段和结构相比,尾端可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

枚举 

枚举枚举,顾名思义,把可能的取值一一列举,被列举的元素叫做枚举常量。

枚举类型的定义:


	enum Day
	{
		Mon,
		Tues,
		Wed,
		Thur,
		Fri,
		Sat,
		Sun
	};

需要注意的是:1、枚举的分割用逗号;2、第一个元素不初始化默认为0, 且一次递增1。也可以在定义的始后赋初值。

枚举的使用:

enum Day
	{
		Mon,
		Tues,
		Wed=4,
		Thur,
		Fri,
		Sat,
		Sun
	};
	enum Day a = Thur;//枚举变量的赋值
	a = 5;//重新赋值

注意:只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。

联合体

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

#include <stdio.h>
int main()
{
	union A
	{
		char age;
		int id;
	};
	union A a;//联合体变量的定义
	printf("%d\n", sizeof(a));
	//大小为4
}

大小为4,联合体的特点就是,它的成员是共用同一块内存空间的,所以一个联合体的大小,至少是最大成员的大小。再一个就是,当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。 

union A
	{
		char c[5];//5
		int id;   //4
	};
	printf("%d\n", sizeof(union A));
	//联合体大小为8

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值