结构体

118 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情


为什么要创建结构体类型?在我们处理复杂对象的时候,比如描述一个人的时候,它有名字,性别,身高,体重等一些方面的特征。用结构体打包描述的时候就比较方便。

结构体类型的声明

结构体类型的关键字struct。 声明的基本模板为:

struct 标签 { 成员; }变量; 结构体的成员可以是不同的类型。

==结构体类型的特殊声明:== 匿名结构体类型,它只能使用一次。

struct
{
	int a;
	char b;
}x;
struct
{
	int a;
	char b;
}*p;

p=&x这样写是错误的,在编译器看来,它们俩是不同的类型。 在这里插入图片描述

看下面这两种:

struct
{
	int a;
	int b;
}x;
这里的x是一个结构体类型的全局变量
typedef struct
{
	int a;
	int b;
}x;
这里的x是结构体类型。
typedef struct stu
{
	int a;
	int b;
}*put,stu;
这里的put等价于struct stu*类型
stu等价于struct stu类型

结构的自引用

例如:

typedef struct node
{
	int x;
	struct node* p;
}node;
这个自引用就是正确的。
typedef struct node
{
	int x;
	node* p;
}node;
这种就是错误的,这个问题的出现就和先有鸡还是先有蛋的问题一样。
不能知道是先重命名了还是先创建的。

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

  • 结构体变量的定义
struct student
{
	char name[20];
	int age;
}a,b;
//这种是一边声明,一边定义变量的,这里的变量a,b为全局变量
struct student c;
//这种定义的变量是全局变量
int main()
{
	struct student d;
	//这里的d为局部变量
	return 0;
}
  • 简单的初始一下:(应该没有难道吧)
struct student
{
	char name[20];
	int age;
}a={"zhangshan",20}, b={"wuanwu",18};
struct student c={"hh",0};
int main()
{
	struct student d={"sb",88};
	return 0;
}

🟥结构体内存对齐

如何计算结构体的大小,就需要知道它在内存中是如何储存的。 而结构体在内存中存在结构体对齐的现象。

1.第一个成员变量放在偏移量为0的位置 2.后面的成员放在偏移量为对齐数的整数倍的位置。 3.对齐数:编译器默认的一个对齐数与成员大小的较小值 vs的默认对齐数位8 4.结构体的总大小为每个成员默认最大对齐数的整数倍。 5.如果含有结构体嵌套的情况,镶嵌的那个结构体的对齐数是里面成员的最大对齐数。

  • 下面仔细讲解一下:
	struct S1
	{
		char c1;
		int i;
		char c2;
	};
	printf("%d\n", sizeof(struct S1));

	struct S2
	{
		char c1;
		char c2;
		int i;
	};
	printf("%d\n", sizeof(struct S2));

	struct S3
	{
		double d;
		char c;
		int i;
	};
	printf("%d\n", sizeof(struct S3));

	struct S4
	{
		char c1;
		struct S3 s3;
		double d;
	};
	printf("%d\n", sizeof(struct S4));

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

按上面的分析:可知结构体的大小分别为:12,8,16,32 运行看一下情况: 在这里插入图片描述

修改默认对齐数

我们用pragma修改默认对齐数

例子:

#pragma pack(4)
//这里面的数字表示的是默认对齐数
struct p
{
	int a;
	int b;
	char c;
};
#pragma pack()

结构体传参

在结构体传参的时候,最好选择传址调用,有两个好处 1.可以减少对空间的浪费 2.可以对里面的数据进行修改

简单的例子:

#include <stdio.h>
struct student
{
	char name[20];
	int age;
};
void f(struct student* p)
{

}
int main()
{
	struct student p;
	f(&p);
	return 0;
}

结构体实现位段

位段的实现和结构体类似,只不过位段的成员的类型只能是 unsigned int 或者int类型,char类型的也可以。 每个成员名后面要加上:和数字

举个简单的例子:

struct stu
{
	int a : 4;
	int b : 2;
};

后面的数字表示bite位。位段不存在对齐。 位段不具有跨平台性: 1.位段中没有规定在内存使用的过程中,是从左使用还是从右使用。 2.不能满足下一个成员使用的空间是舍弃还是保留的问题没有规定。 3.int位段中无符号还是有符号的问题没有规定

结构体实现位段的内存分配

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
struct S s = { 0 };
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;

在这里插入图片描述

枚举

枚举类型的关键字为enum,枚举就是把所有的可能列举出来。 里面的叫做枚举常量。它们也是有值的,如果没有给他们初始化。默认第一个是0,后面的依次增加。

例子:

enum colour
{
	//注意后面是逗号,
	red,//值是0
	yellow,//值是1
	green//值是2
};
enum colour
{
	red=2,//这种不是赋值,而是给这个常量一个初始值。
	yellow=5,
	green//它的值为6
};

联合(共用体)

联合类型的定义

关键字union。 里面的成员都是占用同一块空间。

例子:

union people
{
	char a;
	int b;
};

联合大小的计算

联合体可能是最大类型所占空间的大小。 当结构体的大小不是最大对齐数的整数倍时,需要对齐。

例子:

union Un1
{
	char c[5];
	//开辟了5个字节的空间
	int i;
	//i占4个大小的空间,开辟的空间够用
	//共5个字节的空间,但是不是4的整数倍,存在内存对齐,
	//最终为8个字节的大小
};
union Un2
{
	short c[7];
	int i;
	//开辟的14个字节的空间也不是4的整数倍,需要对齐。
	//最终的大小为16个字节
};
int main()
{
	printf("%d\n", sizeof(union Un1));
	printf("%d\n", sizeof(union Un2));
	return 0;
}

看结果: 在这里插入图片描述