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

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

  • 一:结构体的进阶知识
  • 二:位段
  • 三:枚举
  • 四:联合(共用体)

一:结构体的进阶知识

1.1结构体的基础知识
结构体是一些值的集合,这些值被称为成员变量。结构体的成员变量可以是不同类型的。

1.2:结构体的声明

struct tag//tag:结构体名
{
      member-list//member-list:结构体成员变量
}variable-lisr;//variable-lisr:结构体变量名

例如:描述一个人

struct Peo{
	char name[20];//人名
	int age;//年龄
	char id[18];//身份证
}peo;//分号不能丢

1.3:结构体特殊的声明
在声明结构体的时候,可以不完全声明,被称为匿名结构体类型,该结构体只能在结构体分号前创建变量,并且该匿名结构体只能使用一次。
样例:

struct 
{
	char name[20];
	int age;
	char id[18];

}s1 = {"zhangsan",18,"123456789"};
int main()
{
	
	printf("%s %d %s", s1.name, s1.age, s1.id);
	return 0;
}

匿名结构体即使内部结构体变量完全相同,但编译器任会认为它们两个声明是不相同的类型
样例:

struct 
{
	char name[20];
	int age;
	char id[18];

}s1;
struct
{
	char name[20];
	int age;
	char id[18];

}*s2;
int main()
{
	s2 = &s1;
	return 0;
}

在这里插入图片描述
1.4:结构体的自引用
结构体自引用,可以在结构体内创建一个同类型的结构体指针,根据该指针找到下一个结点
自定义类型:结构体、枚举、联合_第1张图片

struct Node{
	int age;
	struct Node* next;//同类型结构体指针
};
int main()
{
	struct Node n;

	return 0;
}

1.5结构体变量的定义和初始化
(1)创建全局变量方法与初始化

//方法一:
struct Stu
{
	int data;
	char id[20];
}s1 = {18,"123456789"},s2;//全局变量
//定义变量时可以同时初始化或者不初始化也许

//方法二:
struct Stu
{
	int data;
	char id[20];
};
struct Stu s3 = { 20, "123478569" };//全局变量
struct Stu s4;
//定义变量时可以同时初始化或者不初始化也许

(2)创建局部变量方法与初始化

struct Peo
{
	int age;
	char name[20];
};
int main()
{
	struct Peo p = { 18, "zhangsan" };//局部变量初始化
	struct Peo p1;//局部变量
	struct Peo p2={.name="zhangsan",.age=20};//通过.成员名可以按照自己的方式初始化赋值
}

1.6结构体内存对齐
(1):结构体内存对齐规则
1、第一个成员直接对齐到结构体变量起始为0的偏移处4,可用offsetof查看对其数位于起始位置的哪个偏移量

#include<stdio.h>
#include<stddef.h>
struct Stu
{
	int a;
	char b;
	float c;
};
int main()
{
	struct Stu s;
	printf("%d\n", offsetof(struct Stu, a));
	printf("%d\n", offsetof(struct Stu, b));
	printf("%d\n", offsetof(struct Stu, c));
	printf("%d\n", sizeof(s));
}

自定义类型:结构体、枚举、联合_第2张图片

2、第二个成员要对齐到某个对齐数整数倍的偏移处
对齐数:结构体成员自身大小和默认对齐数的较小值
vs默认对齐数:8
linux环境不设对齐数(对齐数就是结构体成员的自身大小)
自定义类型:结构体、枚举、联合_第3张图片

3、结构体的总大小必须是最大对齐数的整数倍(最大对齐数就是每个结构体都有一个对齐数,其中最大的对齐数就是最大对齐数)
自定义类型:结构体、枚举、联合_第4张图片

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

#include<stdio.h>
#include<stddef.h>
struct Stu
{
	int a;
	char b;
	float c;
}s;
struct S
{
	int a;
	struct Stu s;
	char b;
};
int main()
{
	printf("%d\n", offsetof(struct S, a));
	printf("%d\n", offsetof(struct S, s));
	printf("%d\n", offsetof(struct S, b));
	printf("%d\n", sizeof(struct S));
}

自定义类型:结构体、枚举、联合_第5张图片
自定义类型:结构体、枚举、联合_第6张图片

1.7修改默认对齐数

#pragam pack(num)//num为要修改的默认默认对齐数
#pragam pack()//将修改的默认对齐数取消,还原为原来的默认对齐数

自定义类型:结构体、枚举、联合_第7张图片
1.8结构体传参
形参是实参的临时拷贝,函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销,如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。因此结构体传参时尽量选择址传参,这样可以减少时间和空间浪费

二:位段

2.1位段是什么
1、位段的声明和结构体类似,但是位段的成员必须时 int、
unsiged int、signed int、char类型的
2、位段的成员后边有一个冒号和一个数字,冒号后边的数字是表示该成员的变量的大小(单位:bit)
3、C语言中没有专门的位段类型,位段的定义要借助于结构体,即以二进制位为单位定义结构体成员所占存储空间

struct Stu
{
	int _a : 2;//开辟2bit空间大小
	int _b : 3;//开辟3bit空间大小
	int _c : 16;//开辟16bit空间大小
	int _d : 30;//开辟30bit空间大小
};
int main()
{
	printf("%d\n", sizeof(struct Stu));
}

自定义类型:结构体、枚举、联合_第8张图片

2.2位段的内存分配
1、位段的成员可以是int、unsigned in、singned int或者char类型。位段的空间上是按照需要以4字节(int)或者1个字节(char)的方式开辟的

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

3、不允许位段跨越一个字节的边界,如果一个字余下的空间不能容纳一个位段,则这个位段从相邻的下一个字的边界开始存放。由此在上一个字中留下未用的空位,称为空穴。(16位系统一个字是2个字节,32位系统一个字是4个字节,64位系统是8个字节。)
样例:vs2013平台测试
自定义类型:结构体、枚举、联合_第9张图片
a的二进制为1010,但由于a只有3bit空间只能存放3位,因此a的值为2
b的二进制为1100,b有4bit空间,但是最高位被定义为符号位,数据位就为100,因此b的值为-4
c、d都由于开辟的空间不够存放所以都需要重新开辟一块空间存放,未使用的区域则就浪费了

4、位段的特点
●位段可以没有名字,无名位段表示该空间不用。

●位段只能作为结构体成员,不能作共用体的成员。

●位段没有地址,对位段不能进行取地址的“&”运算。

●位段的长度不能大于机器字长度,也不能定义位段数组。

●位段可以用整型格式符输出。

●位段可以在数值表达式中引用,它会被系统自动转换成int型。

5、位段与结构相比,位段也可以达到同样的效果,但是位段可以更好的节省空间,但存在跨平台问题
自定义类型:结构体、枚举、联合_第10张图片

2.3位段的跨平台问题
1、int 位段被当成有符号数还是无符号数是不确定的
2、位段中最大位的数目不能确定(16位机器最大16位,32位机器最大32位,若在16位机器中写成20则就会发生报错)
3、位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义
4、当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的

三:枚举

3.1枚举是什么
枚举顾名思义就是一 一列举,把可能的取值一 一列举,在C/C++中枚举是一个被命名的整型常数的集合。可以用枚举表示的例如一周的周一到周日,月份等等

3.2枚举类型的定义
1、枚举关键字enum,枚举与结构体类似但是枚举的成员是以“,”逗号分隔的,结构体成员变量是以“;”分号分隔的,枚举的成员变量是没有类型的,最后一个成员变量可以 不用“,”

enum month//enum month:是枚举类型;month:是枚举名
{
	mon,
	tues,
	wed,
	thur,
	fri,
	sat,
	sun
};

2、枚举中的成员也被称为枚举常量,枚举常量在默认未初始的情况下都是从0开始依次递增1。也可以给枚举常量赋一个初始值,后面的成员也是依次递增1。
自定义类型:结构体、枚举、联合_第11张图片
3.3枚举的优点
1、增加代码的可读性和可维护性
2、和#define定义的标识符比较枚举有类型检查,更加严谨,#define定义的表示在编译的过程中就会替换,无法在调试时更精确的监视
3、防止命名污染(封装)
4、便于调试
5、使用方便,一次可以定义多个常量

四:联合(共用体)

4.1联合类型的定义

1、联合是一种特殊的自定义类型,联合体关键字union

union Un//Un:联合体名
{
	char a;
	int b;
	double c;
};

4.2联合体的特点
1、联合体的成员是共用同一块空间,联合体的大小至少是最大成员的大小
自定义类型:结构体、枚举、联合_第12张图片
2、联合体因为是共用同一块空间,使用的时候一次只能访问初始化的时候一次只能初始化一个成员,其他成员没有初始化时也可以访问到其中一个初始化的值,根据下面样例就可以得出联合体是共用同一块空间

union Un u={'a',5,4.5}//该初始化是错误的,编译的时候会直接报错

自定义类型:结构体、枚举、联合_第13张图片
3、联合体的大小计算
联合体的大小至少是最大成员的大小,当联合体成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数
自定义类型:结构体、枚举、联合_第14张图片

以上就是我对结构体、枚举、联合三种自定义类型的学习总结,若有错误或有补充的知识点欢迎大家在评论区指出,我将会马上修改。欢迎大家一起来学习编程、分享编程知识!!!

你可能感兴趣的:(c语言,visualstudio)