自定义类型:结构体,枚举,联合

1、结构体

1.1 什么是结构体

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

在C语言中,数据类型是有限的,但人的需求是无限的,当我们需要存储更多不同的复合类型的数据时,我们就要用到结构体。比如说:我们要记录个人信息——姓名、性别、年龄和联系电话,此时我们就可以用到结构体。

1.2 结构体的声明

struct perinfo  //个人信息
{
	char name[20];
	char sex[6];
	char tel[12];
	int age;
};//注意:分号不能丢

struct Book   //嵌套定义
{
		char name[20];
		char id[20];
		float price;
		struct perinfo writer;  //嵌套了结构体 struct perinfo
};

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

struct perinfo  //perinfo这个结构体是可以被除main函数以外的函数调用
{
	char name[20];
	char sex;
	char tel[12];
	int age;
}s1 = {"lisi",'m',"13455687546",18};//可以在声明结构体时定义结构体变量以及初始化结构体变量
int main()    
{
	struct Book   //Book这个结构体只能被被main函数调用,只能在main函数中定义变量
	{
		char name[20];
		char id[20];
		float price;
	};
	struct perinfo s2 = { "asir",'f',"13455684546",28 };//也可以在函数中定义结构体变量以及初始化结构体变量
	return 0;
}

1.4 结构体内存对齐

首先我们得知道什么是偏移量。

偏移量是以字节为单位,现在所处的地址距离结构体起始地址之间的字节个数。

计算结构体所占内存的大小:

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

举例:

计算以下结构体所占内存的大小

struct s1  
{
		char c1;
		char c2;
		int b;
};

从偏移量为0到偏移量为7总共有八个字节,而结构体最大对齐数为4(一个是1一个是4),八为4的倍数,所有结构体占八个字节。

struct s1
{
	char c1;
	char c2;
	int b;
};

struct s2    //嵌套结构体
{
	char c1;
	struct s1 s2;
	int b;
};

最终结构体的大小为32个字节。

1.5 结构体传参

结构体传参有两种方式:

  1. 用一个结构体作为形参,将整个结构体传过去
  2. 用一个结构体指针作为形参,把结构体的地址传过去

 举例:

#include<stdio.h>
struct S1
{
	int a[10];
	int c;
};

//把整个结构体传过去
void Print_struct1(struct S1 ss)
{
	printf("%d\n", ss.c);
}

//把结构体的地址传过去
void Print_struct2(struct S1 *ss)
{
	printf("%d\n", ss->c);
}

int main()
{
	struct S1 s = { {0},1 };
	Print_struct1(s);
	Print_struct2(&s);
	return 0;
}

显然,只把结构体的地址传过去更省力;传地址,则只需要再创建一个结构体指针,只需要四个字节(32位环境下);而把结构体传过去,需要创建一个结构体来接收,会浪费内存和时间。

2、位段

2.1 什么是位段

位段有以下要求:

  1. 位段的成员必须是整形家族的数据,如 int 、unsigned int 、signed int 、char型数据
  2. 位段的成员名后有一个冒号和一个数字

位段,顾名思义,是以位为单位的, 成员名后面的数字就是表明这个成员占了多少位。

位段写法跟结构体很像

举例:

struct s
{
	int a : 4 ;  //成员a原本应该占32位,现在占4位
	char b : 5;  //成员b原本应该占1位,现在占4位
	char c : 6;  //成员从c原本应该占1位,现在占4位
};

2.2位段的内存分配

如何计算位段所占字节数,下面我们以VS2017为例

#include<stdio.h>
struct s
{
	char a : 2;
	char b : 4;
	char c : 5;
};

int main()
{
	struct s s1;
	s1.a = 5;
	s1.b = 3;
	s1.c = 4;
	printf("%d",sizeof(s1));
}

这个程序输出的结果是2,说明这个位段占了两个字节,那么是怎么分配的呢?让我们来看看。

 

 注意,这里涉及到一个大小端存储的问题,这个问题我在之前的博客中也有提到。在VS中,一个字节内部是大端存放,即低地址存到高地址处,高地址存到低地址处,而在字节与字节的数据存放中又是采用小端存放,即低地址存到低地址处,高地址存到高地址处。s1中,地址由低到高,对应的数据为 c d c 4,转成十六进制的形式,为0xc4cd。

以上的分配仅适用于VS,位段的存放方式有许多不确定性的。

2.3 位段的跨平台问题

位段的存放方式有许多的不确定性:

  1. int位段被当成有符号数还是无符号数不确定
  2. 位段中最大位(也就是int的位数)不确定,在16位机器中最大位16位,也就是两个字节,在32位和64位机器中最大位为32位,也就是4个字节)
  3. 位段中的成员在内存中是从左往右分配,还是从右往左分配的,也是不确定的
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余的位还是继续利用,这也是不确定的

因此,虽然位段能够节省空间,但是位段的跨平台型能力较差,具有很多的不确定性。

3、枚举

3.1 枚举的定义

enum color
{
	red,
	green,
	purple
}col;

enum color 为枚举类型名,red,green,purple为枚举常量,用enum color定义了一个枚举变量,叫作col。

注意:

  1. 枚举常量也就是枚举元素的名字本身并没有特定的含义,例如不会因为写成sum患者Sunday就自动代表“星期天”,它只是一个负号,便于程序员理解
  2. 在没有对枚举常量初始化时,第一个枚举常量为0,且后一个的枚举常量比前一个枚举常量的值大1。在上面的声明中,编译系统按照定义给red,green,purple赋值为0,1,2。如果对其中的枚举常量初始化,它们的值就会有改变。
    enum color
    {
    	red=5, //red的值为5
    	green,//green的值为6
        purple //purple的值为7
    };
    
    enum color
    {
    	red, //red的值为0
    	green=5,//green的值为5
        purple //purple的值为6
    };
    
    enum color
    {
    	red, //red的值为0
    	green,//green的值为1
        purple=5 //purple的值为5
    };
    

  3. 枚举元素是常量,不能对它们赋初值。

    enum color
    {
    	red=5, //这是正确的 
        green,
        purple 
    };
    red=0;//这是错误的 
    

4、联合(共用体)

4.1 联合体的定义

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

union Un
{
	char c;
	int a;
};
union Un un;
int main()
{
	printf("%d", sizeof(un));
	return 0;
}

4.2 联合大小的计算

联合有以下特点:

  1. 联合的大小至少是最大成员的大小
  2. 当最大成员的大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍处。
union Un1
{
	char c[5];
	int a;
};

union Un2
{
	char f;
	int i;
}; 

int main()
{
	printf("%d", sizeof(union Un1));
	printf("%d", sizeof(union Un2));
	return 0;
}

在union Un1中,最大成员应该是c[5],大小为5个字节,所以联合的大小至少要是五个字节,c[5]这个数组的对齐数为1(1与8比较),a的对齐数是4,所以union Un1的最大对齐数是4,所以联合的大小要为4的倍数,所以最后联合的大小为8个字节。

在union Un2中,最大成员应该是i,大小为4个字节,所以联合的大小至少要是四个字节,联合的最大对齐数为4,所以最后联合的大小为4个字节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值