【C语言】自定义类型详解

结构体

在这里插入图片描述

结构体声明

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

struct B
{
	char c;
	short d;
	double e;
};
struct stu          //结构体成员可以是各种类型例:整形 指针 浮点型还包括结构体等
{
	//成员声明
	struct B b;
	char name[10];
	int age;

}s1,s2;//结构体变量s1,s2;  是全局变量
int main()
{
	//结构体的初始化,  s3是局部变量
	struct stu s3 = { {'w',10,2.3}, "wangwu", 25 };
	printf("%c \n", s3.b.c);
	printf("%lf \n", s3.b.e);
	printf("%s \n", s3.name);
	return 0;
}

在这里插入图片描述

结构体成员可以是各种类型例:整形 指针 浮点型还包括结构体等
运算符( . ) (->)
例:s3.names3->name,都可以

特殊的声明

在声明结构体时,可以不完全声明

在这里插入图片描述

结构体的自引用

结构体自引用就是在自己内部创建同类型的成员变量

struct Node
{
	int data;
	struct Node next;
};

会形成一个死循环。
正确代码

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

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

struct B
{
	char c;
	short d;
	double e;
};
struct stu          **//结构体成员可以是各种类型例:整形 指针 浮点型还包括结构体等**
{
	//成员声明
	struct B b;
	char name[10];
	int age;

}s1,s2;//结构体变量s1,s2;  是全局变量
struct end
{
	char name[10];
	int age;
	char id[11];
}s4 = { {"zhangsan"},54 , 1234567891 };//结构体嵌套初始化
int main()
{
	//结构体的初始化,  s3是局部变量
	struct stu s3 = { {'w',10,2.3}, "wangwu", 25 };//初始化:定义变量的同时赋值
	printf("%c \n", s3.b.c);
	printf("%lf \n", s3.b.e);
	printf("%s \n", s3.name);
	return 0;
}

结构体内存对齐

结构体内存对齐规则

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的值为8 Linux中的默认值为4
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所 有最大对齐数(含嵌套结构体的对齐数)的整数倍。

在这里插入图片描述

因为cl是第一个成员,所以从零地址出开始偏移。cl对齐数为1,a对齐数为4,b对齐数为1

为什么存在内存对齐?

  1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址 处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器 需要作两次内存访问;而对齐的内存访问仅需要一次访问。 总体来说: 结构体的内存对齐是拿空间来换取时间的做法。

在这里插入图片描述

s1和s2类型的成员一模一样,但是s1占了12字节,s2占8字节,所以我们在设计结构体是既要满足对齐又要注重节省空间

修改默认对齐数

#pragma 这个预处理指令,可以改变我们的默认对齐数。

在这里插入图片描述

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

结构体传参

struct S
{
	int data[1000];
	int num;
};

struct S s = { {1,2,3,4},1000 };

void print1(struct S s)//结构体传参
{
	printf("%d\n", s.num);
}
void print2(struct S* ps)//结构体地址传参
{
	printf("%d\n", ps->num);
}
int main()
{
	print1(s);
	print2(&s);
	return 0;
}

上面的 print1 和 print2 函数哪个好些?
答案是:首选print2函数。
原因:函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
结论: 结构体传参的时候,要传结构体的地址。

段位

段位的的声明和结构体类似,但有两个不同

  1. 段位的成员必须是int,unsigned int,signed int,char。
  2. 段位成员后边有一个冒号和一个逗号。
    在这里插入图片描述

位段的内存分配

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

枚举

枚举顾名思义就是一一列举。
把可能的取值一一列举。

在这里插入图片描述

enum Color 都是枚举类型。
{}中的内容是枚举类型的可能取值,也叫 枚举常量 。

在这里插入图片描述

这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。

枚举的优点

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

2.和#define定义的标识符相比枚举有类型检查,更严谨

3.防止命名污染(封装)

4.便于调试

5.使用方便,一次可以定义多个变量

枚举的使用


enum option
{
	EXIT,//0
	ADD,//1
	SUB,//2
	MUL,//3
	DIV//4
};

int main()
{

	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
			case EXIT: 
					break;
			case ADD:
					break;
			case SUB:
					break;
			case MUL:
					break;
			case DIV:
					break;
			default:
					break;

		}
	} while (input);

}

更加直观的显示所要实现算法

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值