【C语言】自定义类型:结构体、枚举、联合

本文详细介绍了C语言中的结构体,包括匿名结构体、自引用、初始化、内存对齐以及结构体传参。此外,还讨论了位段的内存开辟和分配,枚举的特性和用法,以及联合(共用体)的概念和大小规则。最后提到了如何判断系统的大端或小端存储模式。
摘要由CSDN通过智能技术生成

目录

1.结构体

1.1结构体类型

1.2结构体的自引用

1.3结构体的初始化

1.4结构体内存对齐

//对齐

//offsetof  

//修改默认对齐数

1.5结构体传参

 2.位段

2.1位段的内存开辟

2.2位段的内存分配

3.枚举

4.联合(共用体) 

//判断大小端


1.结构体

1.1结构体类型

常见的结构体就不赘述了哈~直接见下图。

匿名结构体:顾名思义,就是没有名字的结构体,在后续进程中不能直接使用,所以要在定义时就创建全局变量。

 

1.2结构体的自引用

小Tips:链表:数据结构——>数据在内存中存储的结构

struct Node
{
	int date;
	struct Node* next;//一个节点中包含同类型的下一个节点的地址
};
int main()
{
	struct Node n1;
	struct Node n2;
	n1.next = &n2;//n1中的next存放n2的地址
	return 0;
}

//重命名
typedef struct Node
{
	int date;
	struct Node* next;//注意这里的struct不能去掉
}Node;

1.3结构体的初始化

struct P
{
	int x;
	int y;
};
struct S
{
	int num;
	char ch;
	struct P p;
	float d;
};
int main()
{
	struct P p = { 1,2 };
	struct S s1 = { 100,'w',{6,8},3.14 };//顺序赋值
	struct S s2 = { .d = 5.2f,.p.x = 2,.p.y = 3,.ch = 'a',.num = 10 };//乱序赋值
	printf("%d %c %d %d %f\n", s1.num, s1.ch, s1.p.x, s1.p.y, s1.d);
	printf("%d %c %d %d %f\n", s2.num, s2.ch, s2.p.x, s2.p.y, s2.d);
	return 0;
}

1.4结构体内存对齐

结构体对齐规则:(拿空间换时间)

1> 结构体的第一个成员对齐到结构体在内存中存放位置的0偏移处;

2> 从第二个成员开始,每个成员都要对齐到一个对齐数的整数倍处;

(对齐数:结构体成员自身大小和默认对齐数的较小值;默认对齐数:VS环境下(与32位或64位无关)为8,Linux gcc 环境下没有对齐数,对齐数就是结构体成员的自身大小);

修改默认对齐数:#program pack() 

3> 结构体的总大小,必须是所有成员(包含嵌套结构体成员)的对齐数中最大对齐数的整数倍

4> 如果结构体中嵌套了结构体成员,要将嵌套的结构体成员对齐到该结构体中本身成员中最大对齐数的整数倍处;

5> 节省空间:尽量让占用空间小的成员集中在一起

//对齐

struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	char c2;
	int i;
};
int main()
{
	printf("%d\n", sizeof(struct S1));//12
	printf("%d\n", sizeof(struct S2));//8
	return 0;
}

 

//offsetof  

如果还不信(bushi,可以用 offsetof 来检验:

格式:offsetof (type,member)

功能:计算结构体中某变量相对于首地址的偏移。

头文件:#include<stddef.h>

 

//修改默认对齐数

#pragma pack(1)//修改默认对齐数为1,相当于不对齐
struct S
{
	char c1;
	int num;
	char c2;
};
#pragma pack()//取消修改的对齐数,恢复为默认8
int main()
{
	printf("%d\n", sizeof(struct S));//6
	return 0;
}

1.5结构体传参

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;
}

以上两种方法,首选print2函数,因为函数传参时,参数需要压栈,时间和空间均有开销。如果传递的结构体对象过大时,参数压栈的系统开销比较大,会导致性能下降。(详见:函数栈帧的创建和销毁)

 2.位段

位:二进制(比特)位

1.位段的成员必须是 int、unsigned int 、signed int 或 char (整形家族);
2.位段的成员名后边有一个冒号和一个数字

3.位段只能在结构体中使用

4.位段在空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的;

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

6.从低位向高位使用,一次开辟一个类型大小,当剩下比特位不够存放下一个成员时,丢弃剩余比特位再开辟新的内存。

功能:与结构体相同,不过更节省空间(不需对齐)。

2.1位段的内存开辟

struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
int main()
{
	printf("%d\n", sizeof(struct A));//8
	return 0;
}

解释:先开辟一个int类型即32个比特位,_a使用2个比特位,剩30个比特位;_b使用5个,剩25个;_c使用10个,剩15个;_d需要30个,余下的比特位不足,舍弃;再开辟一个新的int类型即32个比特位,_d使用30个,剩2个;总共开辟64个比特位,即8个字节。 

2.2位段的内存分配

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	printf("%d\n", sizeof((s)));//3
}

3.枚举

1> 不初始化默认为 0,1,2,…;也可以初始化(负值也可);若一开始初始化后面又不初始化,则后面按照顺序递增一

2> 与#define相比,枚举有类型检查,且也不参与运算;

3> 大小为一个整型大小,4个字节

enum Sex
{
	female=4,//是逗号不是封号
	male,
	secret=3//最后一个没有逗号也没有封号
};
enum Color
{
	red,
	green,
	blue
};
int main()
{
	enum Sex s = male;
	//male = 5;常量不能改
	printf("%d\n", female);//4
	printf("%d\n", male);//5
	printf("%d\n", secret);//3
	return 0;
}

4.联合(共用体) 

定义:一种特殊的自定义类型,包含一系列成员,特征是这些成员共用同一块空间,但是不能同时使用。

大小:1> 联合的大小至少是最大成员的大小
           2> 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍(同结构体)。

union UN
{
	char c;//两个成员共用同一块空间
	int i;//不能同时使用
};
int main()
{
	union UN un;
	printf("%d\n", sizeof(un));//4
	printf("%p\n", &un);//这三个地址相同
	printf("%p\n", &(un.c));//同上
    printf("%p\n", &(un.i));//同上
	return 0;
}

 

union UN1
{
	char arr[5];//5
	int i;//4
};
union UN2
{
	short str[7];//14
	int j;//4
};
int main()
{
	union UN1 un1;
	union UN2 un2;
	printf("%d\n", sizeof(un1));//8
	printf("%d\n", sizeof(un2));//16
	return 0;
}

//判断大小端

int check_sys()
{
	union UN
	{
		char c;
		int i;
	}un;
	un.i = 1;
	return un.c;
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");
	return 0;
}

要开学考啦,加油加油,再创辉煌!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值