结构体、位段、枚举、联合

结构体

概念:将相同或不同数据放在一起的一种聚合数据类型。

声明

struct tag
{
	member - list;
}variable - list; 

对上面的代码的说明:1.tag是结构体的标签或是名称,自己可以命名(见名知意)。可以省略,但是不建议省略。

                                  2.member-list是结构体成员列表(不能为空),且可以是任意类型。struct关键字不能省略。

                                  3.variable-list是变量列表,可以一个或多个,中间用“,”隔开。可以省略,建议省略(按需所取)。

                                  4.variable后面的“;”不能省略。

匿名结构体:(不推荐使用)

结构体1

struct
{
	int a;
	char b;
	float c;
}x;

结构体2

struct
{
	int a;
	char b;
	float c;
}*p;

虽然两个结构体(结构体1和结构体2)的成员都一样,但是x和*p不是同一种类型。结构体之间不能复制。有几个结构体,就有几种类型。

结构体3

struct
{
	int a;
	char b;
	float c;
}x,*p;

此时的x和*p才是同一种类型。

结构体的成员的访问

三种访问方式:1.使用结构体变量名+“.” (eg:  S.name)

                       2.使用指针+“.”       (eg:  (*p).name )

                       3.使用指针指向“-->” (eg: p-->name)

结构体的自引用

代码1(错误使用)

struct node
{
	int data;
	struct node next;
};

代码2(正确使用)

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

总结:结构体的自引用只能用自身结构体类型定义指针变量。(这样才可以给其确定类型,进而开辟空间。)

结构体的不完整声明

代码举例:

struct B;
struct A
{
	int a;
	struct B*pb;
};
struct B
{
	int b;
	struct A*pa;
};

结构体变量的定义和初始化(类比数组就好)

注意:结构体和数组都能不能整体赋值。

结构体的传参   不发生降维问题,结构体传参的时候传的是结构体指针

结构体的内存对齐(重点

结构体内存对齐的规则:

1.第一个成员在与结构体变量偏移量为0的地址处。(第一个成员对齐数为0,默认是对齐了的)

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处(起始偏移量要整除对齐数)。对齐数=编译器默认的对齐数与该成员大小的较小值。(VS默认的值为8,Linux中默认的是4)

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

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。总体来说:结构体的内存对齐是拿空间来换取时间的做法。

下面举例说明:

struct S3
{
	double d;
	char c;
	int i;
};

该结构体的大小为16.分析:double对齐数:8。char对齐数:1。int 对齐数:4。char要对齐到4上,所以要加上3。该结构体的最大对齐数是8, 所以最后结构体的大小为16(8+1+3+4).

struct S4
	{
		char c1;
		struct S3 s3;
		double d;
	};

该结构体的大小为32.分析:char对齐数:1。结构体S3的最大对齐数是8,char要对齐到S3要加上7,又因为S3本身大小为16,所以加上16,还有char自身的大小1,double对齐数为8,所以最终S4大小为32(1+7+16+8)。

位段

位段的声明和结构体是类似的,有两个不同:

1. 位段的成员必须是int,unsigned int, signed int或是char类型。

2.位段的成员后边有一个冒号和一个数字。比如:

struct A
{ 
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
A就是一个位段。int _a:2; 表示_a占用int的2个空间,也只能使用冒号后面数字个比特位。并且_b放置的位置是在_a使用后剩余的空间里。若是剩余的空间不够使用,才会开辟新的空间来使用。所以位段A的大小是8(_a,_b,_c._d总共需要2个int类型的空间) 即就表明了位段的原则是:压缩存储,能挤就挤的原则。

位段的内存分配

1. 位段的成员可以是int ,unsigned int ,signed int 或是char类型。

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

3. 位段涉及很多不确定的因素,位段是不跨平台的,注重可以值得程序应该避免使用位段。

总结:跟结构体相比,位段可以达到同样的效果,可以很好的节省空间,但是有跨平台的问题存在。

枚举

含义:一 一列举。

定义

enum Color
{
	Red,
	Blue,
	Green,
};

枚举常量之间用“,”隔开,枚举类型定义变量,只能被赋值为枚举的常量。{}中的内容是枚举类型的可能取值,也叫枚举常量。这些可能取值都是有值的,默认从0开始,依次递增1,当然在定义的时候也可以赋初值。

枚举的优点

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

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

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

4.便于调试。

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

联合(共用体)

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

联合体大小的计算:

1.联合体的大小至少是最大成员的大小。

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

比如:

union Un
{
	char c;
	int i;
};

un的大小就是4.分析:char的对齐数是1,int的对齐数是4,所以该联合体的大小应该是4.

注意:联合体的所有成员都可以认为是联合体内的第一个成员。并且联合体内所有成员的地址都是一样的。

下面是联合体和结构体的一个巧妙连用:

将long 类型的IP地址转为点分十进制的表示形式:

#include<stdio.h>
#include<windows.h>
int main()
{
	union ip_addr
	{
		unsigned long addr;
		struct
		{
			unsigned char c1;
			unsigned char c2;
			unsigned char c3;
			unsigned char c4;
		}ip;
	};
	union ip_addr my_ip;
	my_ip.addr = 176238749;
	printf("%d.%d.%d.%d\n",my_ip.ip.c4,my_ip.ip.c3,my_ip.ip.c2,my_ip.ip.c1);
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zy_20181010/article/details/80484510