c语言-自定义类型详解(结构体+枚举+联合+内存对齐)

文章目录

    • 结构体(包含内存对齐)
    • 枚举
    • 联合

结构体

1.结构体是什么

        结构体是一个或多个值的集合,每个值是结构体的成员变量,每个成员变量可以是不同的类型的变量。

2.结构体声明

        例如描述一个人(姓名,年龄,性别,身份证号,等等)

struct people     //struct 关键字  people可自定义
{
	char name[10];//名字
	int age;      //年龄
	char sex[5];  //性别
	char id[20];  //身份证号
}; //有分号!

         注意:特殊声明方式(不完全声明)

//匿名结构体类型
struct     
{
	char name[10];
	int age;      
	char sex[5];  
	char id[20];  
};

//1.匿名结构体定义变量只能以下面的方式定义

	struct
	{
		char name[10];
		int age;
		char sex[5];
		char id[20];
	}x;		//定义多个只需在x后增加变量名即可,注意 ; 不可以丢
//或
	struct
	{
		char name[10];
		int age;
		char sex[5];
		char id[20];
	}*P;


//2.下面写法非法,会报错

*p = &x;  

/* 
   1.因为匿名结构体编译器识别时,即使上面两者的成员列表顺序,类型,大小都一致
    也会认为是两个不同的结构体类型。
   2.所以,结构体类型一般只能用一次。
 */

3.结构体自引用

//结构体自应用

/* 错误写法
struct MyStruct
{
	int a;
	struct MyStruct b;
};
   错误的用法,此时编译器访问 Mystruct 结构体类型时,有自调用本身,相当于陷入了无穷循环时。
	   结构体的大小就无法确定。
*/

//正确写法
struct MyStruct
{
	int a;
	struct MyStruct* b;  //定义为指针时,指针大小是固定的。
};

/* 错误写法
typedef struct MyStruct2
{
	int a;
	Ms* b; //这里会出现报错,因为 Ms 是由 typedef(对一个完整类型重命名) 重命名而来,
	       //此时的 MS 相当于还未创建出阿来
}MS;
*/

//正确写法

typedef struct MyStruct2
{
	int a;
	struct MyStruct2* b; 
	
}MS;

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

//定义
	
	struct people
	{
	char name[10];
	int age;
	char sex[5];
	char id[20];
	}p1;  //声明时,直接在结构体后定义变量

	struct people p2; //定义结构体变量
	

//初始化
	struct people2    
	{
		char name[10];
		int age;      
		char sex[5];  
		char id[20];  
	}p1 = {"李四",20,"女","0987654321"};  //声明时,直接在结构体后定义变量 ,同时初始化赋值

	struct people2 p2 = { "张三", 18, "男", "1234567890" }; //定义结构体变量,同时赋值初始化。

//结构体嵌套初始化
	struct AA
	{
		int x;
		char y[10];
	};
	struct BB
	{
		int h;
		struct AA z;
		struct BB* i;
	};

	struct BB stu = { 15, { 14, "chushihua" },NULL };

5. 计算结构体大小 (结构体内存对齐)

       引言:计算结构体大小需了解 对齐规则 和 偏移量     

        5.1 结构体对齐规则

            1. 第一个成员在与结构体变量偏移量为0的地址处。

            2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

            3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

            4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体                  的整 体      大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

             对齐数 = 编译器默认对齐数 与 该成员变量的大小 相比 的 较小值 (不同编译器默认对齐数                              可能不同。例:vs为8)

         5.2 偏移量

                以机构体的起始地址开始,每增加一个字节,从0开始依次+1递增

          5.3 结构体大小计算过程(以vs编译器为例)

成员变量占多少取决于类型大小(例如:int 4个字节,char 1个字节)

结构体类型 S1,S2,成员列表顺序不同,二者大小不同,所以在计算结构体类型是,注意 成员顺序

 

 5.4 为什么存在内存对齐

        大部分的参考资料都是如是说的:

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

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

        总体来说: 结构体的内存对齐是拿空间来换取时间的做法。

5.5  如何设计,满足对齐规则,又尽量避免空间的浪费

        让占用空间小的成员尽量集中在一起 例:

struct MyStruct
{
	char c1;
	char c2;
    int a;
};

 #pragma 预处理指令,可更改默认对齐数。

        # pragma pack(4)   //默认对齐数改为 4.

        #pargma pack()      //还原对齐数为默认

        注:默认对齐数,建议设为2的次方数

6. 结构体传参 

        

struct MyStruct2
{
	int a;
	char c1[100];
	
};

void print1(struct MyStruct2 str) //结构体传参
{
	printf("%d\n", str.a);
}

void print2(struct MyStruct2* str) //结构体地址传参
{
	printf("%d\n", str->a);
}
/*
	建议用地址传参。因为在传参过程中,形参是实参的在栈上的临时拷贝。
	若结构体过于庞大,可能会造成栈溢出。
*/

int main()
{
	struct MyStruct2 str = { 1, "zhangsan" };
	//print1(str);
	print2(&str);
	
	return 0;
}

枚举

1.什么是枚举

         可以列举出来的,有限的值。例如星期,月份,性别,等等。

2.如何定义枚举类型

	enum MyEnum
	{		  //对应的值
		man,      //0
		girl=1,   //1
		unknown   //2
	};

	/*
	1.枚举变量的值,默认从0开始,依次+1递增
	2.也可以初始赋值,例如:girl。
	3.假设,girl 初始赋值为3,那么 unknow 的值则变为4.
	*/

3.枚举类型的使用

int main()
{
	enum MyEnum
	{		  //对应的值
		man,      //0
		girl = 3, //1
		unknown   //2
	};

	enum MyEnum a1 = man;       //保持同类型赋值
	enum MyEnum a2 = girl;
	enum MyEnum a3 = unknown;

	/*
	此时 a1-a3 的值 与 枚举的值相对应
	*/
	printf("%d", a1);		
	printf("%d", a2);
	printf("%d", a3);
	
	return 0;
}

4.枚举的优点

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

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

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

 4. 便于调试

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

联合(共用体)

1.联合类型的定义: 

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

2.联合类型的特点

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

union Un
{
 int i;
 char c;
};
union Un un;
// 下面输出的结果是一样的
printf("%d\n", &(un.i));
printf("%d\n", &(un.c));

 3.联合类型声明

//联合类型的声明
union Un
{
 char c;
 int i;
};
//联合变量的定义
union Un un;

 4. 联合类型的大小

1.联合类型的大小至少是成员变量中最大的大小。

2.当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值