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

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

本篇博客将会对自定义类型中的结构体、枚举、联合的相关知识进行介绍。上正题!

1. 结构体

结构体之声明

声明格式如下:

struct tag
{
	member-list;
}variable-list; //variable-list可写可不写,不写在调用时,需使用 struct tag t 如此语句

比如:

struct Student
{
	char name[20];   //名字
	int age;		 //年龄
	char sex[10];	 //性别
	chaar id[20];	 //学号
};

比较特殊的声明:

在声明结构时,不完全的声明,如

//匿名结构体类型
struct 
{
	int a;
	char b;
	float c;
}x;

结构体之自引用

自引用即在结构中包含该结构体本身的成员,格式如下

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

typedef struct Node2
{
	int data;
	struct Node2* next;
}Node2;

结构体之定义与初始化

结构体的定义与初始化可参照于以下程序,即

struct Node
{
	int x;
	int y;
}n1; 						//声明类型的同时定义变量n1
struct Node n2; 			//定义结构体变量n2

//初始化:定义变量的同时赋初值
struct Node n3 = {x, y};

//结构体嵌套
struct Node2
{
	int data;
	struct Node n;
	struct Node2* next;     //结构体自引用
}n21 = {5, {6,7}, NULL}; 	//声明类型的同时定义变量n21,并初始化
struct Node2 n22 = {8, {9, 10}, NULL};//结构体嵌套初始化

结构体之内存对齐

结构体的内存对齐,多用于计算结构体的大小,而对齐值又可分为四种:

1)基本数据类型的自身对齐值
2)自定义类型的自身对齐值
3)程序的指定对齐值 ,利用 #pragma pack(n) 语句指定对齐值,其中 n为正整数
4)自定义类型的有效对齐值 ==>> 按较小的值对齐

在这四种中,有效对齐值得优先级最高,其次是指定优先级。

直接上程序:

//char 1字节,int 4字节,short 2字节,double 8字节,float 4字节

// 例一
struct S1  // 小 大 小 数据类型排列,两边小的应对齐于中间大的
{
	char c1;//自身1字节,要与int型(4 bytes)对齐,所以其字节数变为 1 + 3 = 4 bytes
	int i;  //因为int型(4 bytes)是该结构体数据类型字节数最多的,所以其他数据类型与它对齐,即以此为对齐标准
	char c2;//要与上述int型对齐,所以扩展为 1 + 3 = 4 bytes
}; //该结构体对齐后的大小为 4 + 4 + 4 = 12 bytes

// 例二
struct S2  // 小 小 大  数据类型排列,前面两个小的总和对齐于大的便可
{
	char c1;// 1 字节 1 + 1 = 2 bytes
	char c2;// 1 字节,其对齐和例一中大致相似,依然是向数据类型最大字节数对齐,但不同点在于该例中只需让两个 char 型合在一起的对齐值等于int型字节数便可,所以 1 + 1 = 2 bytes
	int i; // 4 bytes, 以此为对齐标准
};  //该结构体对齐后的大小为 2 + 2 + 4 = 8 bytes

//例三
struct S3 // 大 小 小 数据类型排列,后面两个小的总和对齐于前面大的便可
{
double d; // 8 bytes,以此为对齐标准
char c; // 1 + 3 = 4 bytes
int i;  // 4 bytes
}; //该结构体对齐后的大小为 8 + 4 + 4 = 16 bytes

//例四-结构体嵌套问题
//此例 s4 中嵌套了一个s3,所以整个结构体字节对齐需加上s3的对齐字节数,但s4中各数据类型对齐时并不是以s3的总字节数对齐,而是s3中最大字节数(8)对齐
struct S4
{
char c1;     // 1 + 7 = 8 bytes
struct S3 s3;// 以 8 bytes为对齐标准,S3对齐字节数为 16 bytes
double d;    // 8 bytes
}; // 该结构体对齐后的大小为 8 + 16 + 8 = 32 bytes

//例五 注意!!!
typedef struct Test   
{
	// 按2字节的倍数对齐
	short a;   // 2 + 6
	struct t    // 4 + 8 + 4  = 16
	{
		double c; // 8 
		int b;    // 4 
		char d;   // 1 + 3
	};
	int e;     // 4 + 4
}Test;
//在Test结构体中,t是新开的结构体,其在 .cpp文件中,不占字节,即不存在对齐字节,此时是以 int型(4bytes)作对齐标准,所以大小为 (2 + 2)+ 4 = 8 bytes
//但,其在 .c 文件中,不会有以上顾虑,依然如正常一样,存在对齐字节,此时是以 double型(8 bytes)作对齐标准,所以大小为 (2 + 6)+ (8 + 4 + (1 + 3)) + (4 + 4) = 32 bytes

修改默认对齐数后的对齐字节数:

以利用 #pragma pack(n) 语句指定对齐值,上程序:


//当不指定对齐字节数时,下述结构体对齐后的大小为 32 bytes
//#pragma pack(1) //指定对齐字节数为 1字节,所以 Test 总的字节数为 2 + 8 + 4 + 1 + 4 = 19 bytes
//#pragma pack(2) //指定对齐字节数为 1字节,所以 Test 总的字节数为 2 + 8 + 4 + (1 + 1) + 4 = 20 bytes
//#pragma pack(10) //指定对齐字节数为 10字节,正常情况算下来的字节数为 50bytes ,但有效对齐值的优先级高于指定对齐值,所以 Test 总的字节数为 (2 + 6)+ (8 + 4 + (1 + 3)) + (4 + 4) = 32 bytes
typedef struct Test    
{
	short a;   // 2 + 6
	struct t    // 4 + 8 + 4  = 16
	{
		double c; // 8 
		int b;    // 4 
		char d;   // 1 + 3
	};
	int e;     // 4 + 4
}Test;

若想在设计结构体的时候,既满足对齐,又节省空间,应做到让占用空间小的成员尽量集中在一起。

结构体之传参

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

结构体之位段

位段的声明和结构是类似的,有两个不同:
1)位段的成员必须是int、unsigned int 或signed int ;
2)位段的成员名后边有一个冒号和一个数字。

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

位段操作的是字节上的 bit 位,可以更节省空间。
格式如:

struct A
{
	int _a:2;
	int _b:5;
	int _c:10;
	int _d:30;
};//该结构体共47位,但由于 int型尾端开辟是以 4字节(32bit) 作为开辟尺度,所以此结构体的大小为 8 bytes

2. 枚举

枚举简单说就是列举,其定义格式常为

enum Node
{
	member-list;
};

以上定义的enum Node 都是枚举类型。 {}中的内容是枚举类型的可能取值,也叫枚举常量。这些常量的可能取值默认从0开始,依次加1,如果在定义时为某个常量赋了值,则后面的常量在其所赋的值基础上,依次加1.

enum Day//星期
{
	Mon,  // 0
	Tues, // 1
	Wed,  // 2
	Thur = 8,  // 8
	Fri,  // 9 
	Sat,  // 10
	Sun   // 11
};
enum Day day = Wed; // day = 2
enum Day day2 = Fri; // day2 = 9

枚举的优点

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

3. 联合

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

同时联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。比如:

//联合类型的声明
union Un
{
	char c;
	int i;
};
//联合变量的定义
union Un un;
//计算连个变量的大小
printf("%d\n", sizeof(un)); // 4 bytes

再例如:

// 默认小端存储方式(大小端可见笔者博客:C语言----数据的存储之杂记)
union Un
{
	int i;
	char c;
};
union Un un;
// 下面输出的结果是一样的吗?
printf("%d\n", &(un.i));
printf("%d\n", &(un.c));
//下面输出的结果是什么?
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);

在上述例子中,默认为小端存储方式(从左往右,由低到高)
联合体是共用一个地址,对其成员赋值时,会一一覆盖,直到最后一次赋值
un.i 低位为11,高位为44
un.i 存储为: 44 33 22 11
un.c存储为: 55
所以,最后un.i被更改为 55 33 22 11

联合大小的计算

联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。如:

union Un1
{
	char c[5]; // 5 bytes
	int i;     // 4 bytes
}; // 8 bytes
//联合体是以其成员中最大数据类型对齐
//Un1 中,即以4字节对齐,但因已有5字节,所以此处会补全到8字节

union Un2
{
	short c[7];  // 14 bytes
	int i;       // 4 bytes
};  // 16 bytes
//同理,此处即以4字节对齐,但因已有14字节,所以此处会补全到16字节

以上便是本篇博客全部内容,有些知识点可能叙述不太清楚,请见谅。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值