struct详解

导入

我们有没有这种情况,总想有一个数组,其中可以有int,double,char。。。各种类型,但是对于内置的数据类型显然是做不到的,于是就有了结构体类型
结构体是将多种不同的结构打包在一起,形成全新的类型进行使用

struct

struct声明以及定义

struct Base
{
	char a;
	int b;
	float c;
	double d;
}name,*p_name;//记得这里的分号,一定不能少
//这里可以一次定义多个变量名
int main()
{
	//也可以后面定义
	struct Base* p;
	
	return 0;
}

同时还有匿名结构体类型

匿名结构体类型

struct
{
	int a;
	int b;
	int d;
};

我们需要特别注意的是,匿名结构体类型只能立即使用,不然就找不到了—无法在后面申请变量

struct
{
	int a;
	int b;
	int d;
}name,*p_name;
int main()
{
	name.a = 20;
	p_name = &name;
}

这样使用都是没有问题的
至于为什么后面就不能使用了,我们可以想一下,声明结构体的时候需要写出结构体的类型,但是你现在只有struct,计算机怎么知道你是那个结构体,所以计算机就给我抛出了警告

结构体的自引用

顾名思义就是自己套自己

struct Base
{
	int a;
	char b;
	struct Base* next;//这种就是自己引用自己
};

在自己引用自己的时候需要注意的是,不能写成下面这种情况

struct Base
{
	int a;
	char b;
	struct Base* next;
	struct Base nn;
};

struct详解_第1张图片
因为根本无法判断这个结构体的大小到底是多少,自己中套自己,俄罗斯套娃一直套下去,根本不知道这个结构体的大小到底是多少,所以如果要自引用的话只能使用指针,指针的大小是一定的 struct详解_第2张图片

struct 的定义和初始化

定义

struct Base1
{
	int a;
	int b;
}p;
int main()
{
	//定义法1---直接定义
	//定义法2---适时定义
	struct Base1* pf;

	return 0;
}

初始化

#include
struct Base2
{
	int a;
	char b;
	struct Base2* next;
};
struct Base1
{
	int a;
	int b;
}p = { 1,2 };
struct Base3
{
	struct Base1 mem1;
	int a;
	struct Base2* p;
};
int main()
{
	//初始化法1---直接初始化
	
	//初始化法2---定义时初始化
	struct Base1 son1 = { 3,4 };

	//含有自引用的初始化
	struct Base2 son2;
	struct Base2 nn = { 1,'a',&son2 };
	struct Base3 mem = { {1,2},3,NULL };
}

初始化的时候根据结构体中的元素进行初始化就行了,遇见结构体嵌套结构体的情况直接使用{}进行初始化

结构体的对齐

开始之前我们先计算一下结构体的大小

#include
struct Base
{
	int a;
	char b;
	double c;
};
int main()
{
	struct Base s;
	printf("%d", sizeof(s));
	return 0;
}

应该会有同学认为因该是int+char+double=13字节
struct详解_第3张图片
我们现在来介绍相应的规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的值为8
    Linux中没有默认对齐数,对齐数就是成员自身的大小
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
    看了没看懂,怎么办,看附图
    例1
    struct详解_第4张图片

struct详解_第5张图片
例2
struct详解_第6张图片
struct详解_第7张图片

struct详解_第8张图片
struct详解_第9张图片

默认对齐数的修改

法一

#pragma pack(8)//默认对齐数为8

法二
struct详解_第10张图片
struct详解_第11张图片

结构体传参

传参的方式是传值和传址
因为结构体大小可能很大,而指针的大小只有4/8个字节,而且地址更容易操作,所以使用传地址的方式进行传递

struct S
{
	int a;
	int b;
	char c;
};
int add(struct S* p)
{
	return p->a + p->b;
}

位段

注意事项

1.位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
2.开辟的时候是根据类型的大小进行开辟的int—4字节,char—1字节
3.位段不能跨平台使用

位段的内存分配

我们已经学习了结构体的内存分配那么我们先来猜一猜位段的内存分配情况
struct详解_第12张图片

#include
struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
int main()
{
	struct S s = { 10,12,3,4 };
	printf("struct S s:%d\n", sizeof(struct S));
	return 0;
}

struct详解_第13张图片
struct详解_第14张图片
每次申请1个字节,如果空间大小小于一个字节数那么每次都必须存储在同一个字节中,否则就需要重新开辟字节,就像c,d,使用c的时候还有3个bit位,但是下一个4bit大于剩余的bit位数,就要开辟新的空间进行存储
但是我们困惑的是他到底是从左向右使用空间的还是从右向左使用空间的呢???
根据观察,计算,vs2019是从左向右使用空间的最后的结果是
struct详解_第15张图片
但是有些编辑器会从右向左进行存储的,所以绝对不能进行跨平台的移植

枚举类型

enum Color
{
	RED,GREEN,WHITE,YELLOW
};

每一个元素都是枚举常量,每个常量都是从0开始递增,同时每个也是可以初始化的

enum Day
{
	MON=1,TUES,WED,THUR,FRI,SAT,SUN
};

在初始化之后的会按照初始化的进行递增
struct详解_第16张图片

注意事项

需要注意的是:枚举类型的变量只能使用枚举的值进行初始化

enum Color//颜色
{
 RED=1,
 GREEN=2,
 BLUE=4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5;               //错误

c语言编译
struct详解_第17张图片
c语言居然能编译的过去,你是不是耍我!!!
不要着急我们来看看c++能不能过得去
struct详解_第18张图片
我们发现了,因为c++检查的更加的严格,所以我们还是只能使用枚举常量进行赋值

联合体(共用体)

公用同一片空间

联合体的声明

union U
{
	int a;
	char b;
};
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,
至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

struct详解_第19张图片
struct详解_第20张图片
总结
struct很强大,要想彻底掌握还需要多多练习,慢慢感悟

你可能感兴趣的:(c++)