C语言自定义数据类型

1结构体

1.1结构体的声明

  结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以使不同类型的变量。
  结构体的通用声明格式为:

struct tag
{
	member-list;
}variable-list;//变量列表通常可以不写。

  例如想要描述一个人,其基本的应该有姓名,年龄,性别等,则对应的结构体为:

struct person
{
	char name[20];//姓名
	short age;//年龄;
	char sex[5];
};//一定要注意这里是有分号的

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

  可以在声明结构体的同时定义变量,即:

struct person
{
	char name[20];//姓名
	short age;//年龄;
	char sex[5];
}p1,p2;

  p1,p2即为定义的变量,但未进行初始化。
  也可以在声明好结构体后,按如下方式定义结构体变量,变量初始化时使用{},里边按照结构体内部成员列表顺序初始化,各成员间以’,'隔开,例如

struct person p1 = {"城南花已开",20,"保密"};//p1即为结构体变量,其类型为struct person

1.3一些特殊声明:

  匿名结构体类型:即在声明结构体变量时省去了tag标签,但此时变量列表variable-list不能省略。应当避免匿名结构体的声明。

1.4结构体的自引用

  结构体内部包含一个类型为该结构体本身的成员与错误的,例如:

struct Node
{
	int data;
	struct Node next;
}

  此时,结构体内部含有自己本身,如果在定义变量时,则会无限的嵌套下去,直到把内存耗尽。
  正确的自引用方式应为:

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

  此时里边放的是同类型的指针,只需要放入一个地址即可找到下一个同类型结构体。

1.5结构体的内存对齐

  结构体内存对齐本质上是为了计算结构体所占的空间大小。
  结构体对齐有如下规则:

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

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

    其中对齐数指的是编译器默认的一个对齐数与该成员所占字节大小的较小值,即
    对齐数=min{编译器的默认对齐数,该成员所占的字节数}
    
  • vs中默认的对齐数为8;(本文使用的是vs编译器)
  • Linux中默认的对齐数为4.
  1. 结构体的总大小为最大对齐数(每个成员都有一个对齐数,且不一定相等)的整数倍。
  2. 如果结构体中嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
    例如
struct S1
{
	char c1;
	int i;
	char c2;
};

  按照上述规定:

  • 第一个成员为char,对齐到结构体变量起始地址的0偏移处。对齐数=min{1,8}=1。
  • 第二个成员为int,大小为4个字节,所以对齐数=min{4,8}=4,所以第二个成员对齐到起始地址的4偏移处。
  • 第三个成员为char,大小为1个字节,所以对齐数=min{4,8}=1,所以第二个成员对齐到起始地址的8偏移处。
  • 所有对齐数最大的为4,所以最终该结构体占用的内存空间大小为 3 × 4 = 12 3\times4=12 3×4=12.如图所示。
    结构体所占空间示例图
图1.1

  可以看出,由于内存对齐,其中空白部分的内存均被浪费掉,因此我们在定义结构体时尽量将占用字节数小的放在一起。即可以把S1改成如下方式:

struct S1
{
	char c1;
	char c2;
	int i;
};

  修改默认对齐数:可以使用预处理指令#pragma改变默认对齐数。即:

#pragma pack(n)//n为我们自己重新定义的对齐数
#pragma pack()//取消设置默认对齐数,将其还原为系统默认值

  应当注意,在修改默认对齐数时,n应为2的幂次值,即 n = 2 x , x = 0 , 1 , . . . n=2^x,x=0,1,... n=2x,x=0,1,...

1.6结构体传参

  结构体传参的时候,应当传结构体的地址。

2位段

  位段与结构体的声明类似,但有两点不同:

1. 位段的成员必须是`char、int、unsigned int或signed int`。
2. 位段的成员名后边有一个冒号和一个数字。该数字表示该成员所占比特位的大小。</font>

  位段的内存分配有如下规则:

  1. 位段的成员以4个字节(int)或1个字节(char)的方式开辟内存(取决于成员本身)
  2. 位段有许多不确定性因素,即不同的平台使用方式不同,例如当前空间不够下一个成员使用时,系统会再开辟空间,但该成员是先把上一块空间使用完再去使用下一块空间还是把上一块空间剩余部分丢弃掉直接使用新开辟的空间,对于不同的编译器,其方式不尽相同。因此,对于可移植性程序,要避免使用位段。

例如:

struct A
{
	int _a:2;//成员是int,系统开辟了4个字节(32个bit位),该成员使用2个,剩余32-2=30个
	int _b:20;//使用上边剩下的30个,还剩10个
	int _c:15;//上边剩余10个,不够用,再开辟一块4字节的空间
}

3枚举

  枚举又叫列举,顾名思义,可以一一列举的变量(又或者说任何可以穷举出来的变量)。例如人的性别有男女,一年有12个月,可以列出来,同样的,一周有七天也可以列出来,三原色红绿蓝也可以列举出来。

3.1枚举的定义

  同结构体声明类似,枚举的定义如下

enum tag
{
	member-list,//各成员间以逗号分隔
};分号必不可少

  例如:三原色

enum Color
{
	RED,//一定要注意是逗号不是分号
	GREEN,
	BLUE
};这里分号必不可少

  枚举的成员又叫枚举可能的取值,即RED,GREEN,BLUE均为Color可能的取值,这些取值默认从0开始,每次递增1,即RED对应的值为0,GREEN对应的值为1。我们也可以自己指定这些成员变量对应的值,即

enum Color
{
	RED =20,
	GREEN,
	BLUE =30
};

  此时RED对应的值为20,GREEN对应的值为21,而BLUE对应的值为30.
  枚举具有如下优点:

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

4联合体(共用体)

4.1联合类型的定义

  联合是一种特殊的自定义类型,这种类型变量包含一系列的成员,特征是这些成员公用一块空间(正因为这个特性,联合体又被称作共用体)。例如:

//联合类型的声明
union Un
{
	char c;//各成员间以分号相隔开
	int i;
};//这里的分号也不能省略

//联合变量的定义
union Un un

4.2联合体所占空间的大小

  由联合体的定义可知,联合体内成员公用一块空间,所以联合体的大小至少为联合体内最大成员的大小,当最大成员的大小不是最大对齐数的整数倍的时候,联合体就要对齐到最大对齐数的整数倍。

例如:

union Un1
{
	char c[5];
	int i
};

  最大成员为c[5],所占内存为 5 5 5个字节,c[5]的对齐数为 1 1 1i的对齐数为 4 4 4, 5 5 5不是 4 4 4的倍数,所以该联合体最终占 8 8 8个字节。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值