C语言——初识结构体(深入篇)

        在对结构体有了较为明晰的认识后,我们当然要进行下一步更深层的认识

目录

1.匿名结构体

2.结构体的自引用

2.1错误的自引用方式:

2.2正确的自引用方式

3.结构体的内存对齐规则


1.匿名结构体

我们知道,数组有不完全初始化,未初始化的数值默认为0,而结构体则有其不完全声明的特殊情况存在——称之为匿名结构体类型

struct 
{
    char name[20];
    float height;
    int age;
}s1,s2;

这类结构体的特点有两个:

①匿名结构体会省略结构体标签(只有struct来定义结构体类型)。

②变量只能定义在大括号后面而不能重新定义结构体的局部变量。

2.结构体的自引用

结构体成员可以包含其他的结构体,那包含一个为该结构本身的成员是否可以呢?

2.1错误的自引用方式:

struct Node
{
    int n;
    struct Node n;//该结构体自身作为成员
};

我们在vs上进行编译发现它也会报错

        因为这种声明是一个循环往复的套娃过程,成员n是一个结构体,n的内部还会有成员是结构体,在给该结构体分布内存时候,由于一直循环,该Node结构体大小是无法确定的,所以这种声明是非法的。

2.2正确的自引用方式

struct Node
{
    int a;
    struct Node *n;//以指针作为成员变量
};

        我们以指针来作为该成员变量自引用,由于指针的长度是确定的,所以编译器在编译过程可以确定该结构体成员的大小,内存分配合理,及成功。当然为了简洁我们也可以将struct Node重新定义一下,也是没有什么问题的。

typedef struct Node
{
	int a;
	struct Node* n;
}Node;
int main()
{
	 Node n;
	return 0;
}

3.结构体的内存对齐规则

        我们在自引用中提到了结构体内存大小这个东西,那么问题来了,对于int来说大小为4字节,char来说为1字节,我们是很明确的,那么结构体的大小应该怎么计算呢?这就要说到我们的结构体内存对齐规则。

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

这便是结构体内存对齐的规则,我将用实例来清楚解释该规则的适用情况,在此之前我们先来介绍一个宏——offsetof,这个宏是用来计算结构体成员相较于起始位置的偏移量

        这个宏需要引用头文件stddef.h,包含两个参数可以返回结构成员变量的偏移量,第一个参数放置结构体类型,第二个放置所求偏移量的结构体成员,如下操作:

我们可以看到对于srtuct s1结构体来说,我们的三个成员的偏移量分别是0、4、8。我们用一个空格作为一个字节来存储数据,具体情况如下:

①:s1的第一个成员char c1我们放在偏移量为0的地址处

②第二个int类型的变量放到“对齐数”整数倍的偏移量处(在vs编译环境,系统默认是对齐数8,而int的对齐数就是其类型大小4,我们选取较小的——4为对齐数)而4倍数的偏移量就有4,所以从偏移量为4的位置开始放置变量i。

③接下来放置c2变量,由于它的对齐数是其变量大小——1<8,所以只需紧挨变量i放置即可,放在我们偏移量为8的位置。

④接下来成员放置完成后计算结构体内存大小,而要求是最大对齐数的整数倍——也就是4的整数倍,我们算下来的是9,所以要变为4的整数倍也就是12,延续到偏移量为11的位置处——这便是按照内存对齐规则计算出来的s1的结构体大小。

(但此时对于s1来说,偏移量为1,2,3位置的空间被浪费了)

s2的大小计算我们如下图所示:

 (同样对于s2来说,我们偏移量为2,3的空间也被浪费掉了)

当我们结构体内嵌套一个结构体的时候,大小又是多少呢,我们如下举例:

struct s3
{
	double a;
	struct s1;
	char ch;
};

我们将struct s2放入新定义的struct s3中时,s3的内存大小是多少呢,分析如下:

 我们在vs环境下编译也可得到s3的大小确实是24个字节

 当然我们也可以将编译器的对齐数修改,只需使用预处理指令#pragma来改变系统的默认对齐数

例子如下:

#pragma pack(1)//修改默认对齐数为1
struct s1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//恢复默认对齐数

#include<stdio.h>
int main()
{
	printf("%d\n", sizeof(struct s1));
	return 0;
}

我们依照上例可以算出它的大小变为了6个字节,当我们结构体对齐方式不合适的时候,我们就可以修改其默认对齐数。这便是小编对于结构体的认识啦~,写的不好也希望大家多多意见咯。希望大家多多支持。

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值