结构体小剖析

前言

在C语言中有很多的内置的数据类型,方便我们去使用,比如整形,字符型,浮点型等等,还有短整型,长整型,就不一一列举了。但是创建这些类型的变量,变量的类型就只能是它们,那有没有一种数据类型,可以存储不同的数据呢?当然有的,就是结构体了。

	int i = 0;
	char c = 0;
	float f = 0.0;
	double d = 0.0;

一,结构体的创建和初始化

结构体是C语言里提供的一种自定义类型,是一种值的集合。结构体里可以包含不同的数据类型,包括但不限于指针,内置数据类型,标量,数组,甚至可以是其他的结构体,即结构体的嵌套。这些值称为结构体的变量,也叫成员变量。在创建结构体的时候,我们需要使用一个新的关键字struct,那怎么创建,有哪些创建的方法呢,如何去进行一个初始化呢。

//结构体的声明,里面含有一个int类型的变量
//后面的分号不可以丢,struct S1是结构体的名称
struct S1
{
	int i;
};

//如果struct后面没有加名称,那么表示这是一个匿名结构体,只能使用一次
struct
{
	char c;
	int i;
};

有了声明,那如何创建一个结构体类型的变量呢

struct S1
{
	int i;
}s1;               //声明的同时我创建了一个结构体的变量s1


struct S2
{
	int i;
	char c;
	struct S1//属于结构体的嵌套
};

struct S2 s;        //声明和创建变量分离

在结构体嵌套的时候需要注意,如果嵌套的结构体类型是自身,那么变量的类型应该为结构体指针。

struct S2
{
	int i;
	char c;
	struct S2 a;
};              //错误的,因为它无穷的嵌套下去

struct S2
{
	int i;
	char c;
	struct S2* s3;
};

有了变量,还需要进行初始化

struct S1
{
	int i;
	char c;
}s1 = {10,'A'};               //声明的同时创建了一个结构体的变量s1并给变量初始化了值


struct S2
{
	int i;
	char c;
	char a[10];
};

struct S2 s = {10,'A',"abcdef"};        //声明,后创建变量和赋值

初始化后,这许多变量,需要一一的访问

二,结构体成员的访问

结构体成员的访问需要两个操作符(.)和(->),(.)操作符是用于结构体变量,(->)操作符是用于结构体指针变量,使用方法为:结构体变量名.成员变量名 或者结构体指针变量名->成员变量名。

struct S1
{
	int val;
	char name[10];
}s1;

int main()
{
	struct S1 s1 = { 0,"name" };              //创建变量并初始化
	struct S1* s2 = &s1;                      //创建指针变量,并赋值s1的地址
	printf("%d %s\n", s1.val, s1.name);       //结构体变量使用(.)操作符访问
	printf("%d %s", s2->val, s2->name);       //结构体指针变量使用(->)操作符访问
}

打印的内容均为s1的成员变量的值
打印的内容均为s1的成员变量的值,访问的时候可以不按照成员变量的顺序访问,按需访问即可。

三,结构体的大小

c语言里用内置数据类型创建的变量都有大小,那么自定义类型的结构体也有大小。如果按照上图创建的变量int占四个字节,char数组占10个字节,那么应该是14个字节,使用代码去测试一下
在这里插入图片描述
计算数量类型的大小需要用到sizeof关键字,使用方法为sizeof(类型名)。可以看到,打印结果不是14,而是16,可见并不是我们一开始想的按照成员变量所占字节数的大小进行相加那么简单,在进行结构体变量的大小的计算的时候,需要遵循一个内存对齐的规则。接下来,我们介绍一下内存对齐

四,内存对齐

要知道内存对齐是什么,需要先了解一下偏移量,计算机在存储数据时,分配的是一块连续的空间,偏移量是存储的数据的位置相较于起始位置的大小,具体是多少
,有两种方法,一是使用内置的库函数进行计算,二是使用画图的方法解决。

方法一,库函数,offsetof
方法二,画图,使用库函数可以不需要知道原理,但画图的方法,就需要了解一下内存对齐的规则

内存对齐规则:
1.第一个成员变量放在偏移量为0的地址处
2 .第二个开始,成员变量要对齐到对齐数的整数倍的地址处
3.结构体的总大小是最大对齐数的整数倍
4.如果存在结构体的嵌套,那么嵌套的结构体要对齐到自身成员变量里最大对齐数的整数倍,整个结构体的大小是成员里最大对齐数的整数倍。

上面又提到一个陌生的概念,对齐数,简单来说对齐数就是变量自身大小和默认对齐数的较小值,vs的默认对齐数是8.

在这里插入图片描述
由于本人没有好用的画图板,这里就用excel和文字给大家介绍内存对齐的每一条规则,大家自己也可以尝试画一下加深理解,其中每一个方格子代表一个字节,后面的数字代表偏移量,那么这个结构体就是从浅绿色开始存储。

int占4个字节,根据第一条规则,偏移量0~3的存val,char占一个字节,根据第二条规则,存放时需要对齐到1(对齐数)的倍数,直接存放在下一个字节,short占两个字节,同样根据第二条规则,存放时需要对齐到2(对齐数)的倍数,5不是2的倍数,跳过,6是2的倍数,所有b存在偏移量6和7的位置,同理d存在偏移量为8到11的位置,根据第三条规则,结构体最大对齐数为4,那么结构体的大小应该是4的倍数,根据图形看,结构体的总大小是12个字节,但是浪费了一个字节,12和运行结果也一致。第四条规则,这里就不举例子了,大家可以尝试算一下。

五,结构体的传参

结构体类型的传参分为传值和传址。

struct S1
{
	int num[1234];
	int sum;
};

void Print1(struct S1 s)
{
	for (int i = 0; i < 1234; i++)
	{
		printf("%d", s.num[i]);
	}
	printf("%d", s.sum);
}


void Print2(struct S1* ps)
{
	for (int i = 0; i < 1234; i++)
	{
		printf("%d", ps->num[i]);
	}
}
int main()
{
	struct S1 s1 = { {0},1 };
	Print1(s1);
	Print2(&s1);
}

Print1和Print2都可以打印s1的值,但是哪个更好呢,形参是实参的一个临时拷贝,当数据很大的时候,拷贝很浪费空间。所以Print2更好,因为传地址过去,形参是一个指针,是4或8个字节,开辟的空间小,可以通过指针的加减来跳过数据,来进行打印,也可以通过(->)来访问打印结构体的数据,但是这样可能造成数据被修改,但当我们确确实实需要修改结构体的内容时,传值就做不到了。

关于结构体,就介绍到这里啦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值