自定义类型:结构体进阶学习分享

这篇具有很好参考价值的文章主要介绍了自定义类型:结构体进阶学习分享。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

结构体在C语言中具有重要的意义。它不仅可以封装和组织数据,还可以提供抽象和封装的能力,方便数据的传递和操作,提高代码的可读性和可维护性,是C语言中常用的数据类型之一。本篇博客将详细介绍相关知识。

1 结构体的基础知识

在结构体初阶我们以及详细介绍过了有关结构体的基础。

结构是一些值的集合,这些值被称为成员变量。结构的每一个成员可以是不同类型的变量。

2 结构的声明

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

例如描述一个学生:

struct Stu
{
	char name[20];//名字
	int age;//年龄
	char sex[5];//性别
	char id[20];//学号
};

3 特殊声明

在声明结构体的时候,可以不完全声明。(匿名结构体声明)
比如:

//匿名结构体
struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}*px;

上面的两个结构在声明的时候省略掉了结构体标签(tag)。
那问题来了?

//在上面代码的基础上,下面的代码合法吗?
px = &x;

警告:

  • 编译器会将上面的两个声明当成完全不同的两个类型。所以是非法的。

4 结构的自引用

在结构中包含一个类型为该结构本身的成员是否可以呢?

struct Node//err,比如计算其大小sizeof(struct Node)时,大小是不确定的
{
	int date;
	struct Node next;
};

//正确的自引用方法
struct Node
{
	int date;
	struct Node* next ;
};

注意: 上面结构体类型有时过于冗杂,我们可以采用重命名。

//我们知道结构体可以匿名。那是否可以重命名呢?
//答案是否定的。
//例如下面这段代码,结构体中的Node* next变量还没有创建成功,结构体不是完整的,因此不可重命名
typedef struct//err
{
	int date;
	Node* next;
}Node;

//正确使用
typedef struct Node
{
	int date;
	struct Node* next;
}Node;

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

有了结构体类型,那如何定义和初始化呢?其实很简单。

struct Stu	//声明类型
{
	char name[15];//名字
	int age;//年龄
};
struct Stu s = { "zhangsan",20 };//初始化

struct Point
{
	int x;
	int y;
};
struct Node
{
	int date;
	struct Point p;
	struct Node* next;
}n1 = { 10, {4,5}, NULL };//结构体嵌套初始化

6 结构体内存对齐

接下来我们将介绍一个热门考点:结构体内存对齐
结构体内存对齐规则:

1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数与该成员大小的 较小值
。vs默认的值为8
。Linux中没有默认对齐数,对齐数就是成员自身的大小

3. 结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

6.1 计算结构体大小相关笔试题(基于VS)

笔试题一:

#include <stdio.h>

struct S1
{
	char c1;//对齐到偏移量为0处
	int i;//int占4个字节,VS默认对齐数为8;所以对齐数为最小值:4字节
		  //所以int对齐到偏移量为4后在向后移动4个字节即为。该位置即是int在内存中占据的空间
	char c2;//同理char的对齐数为1。在int后移动一个字节即可。
};
//上述结构体struct S1共占据9个字节,而结构体的大小必须是最大对齐数4(本题中,对齐数分别为1,4,1)的整数倍
//所以结构体还需向内存申请3个字节空间大小,及总大小为12字节。

struct S2
{
	char c1;//第一个变量成员对齐到偏移量为0处
	char c2; //该变量对齐数为1(char为1byte, VS默认为9byte,取最小值)
			 //即在变量c1后申请1字节即可
	int i;//该变量对齐数为4(int为4byte, VS默认为9byte,取最小值)
		  //所以变量i对齐到偏移量为4处,在向后申请4个字节即可。
};
 //到此结构体总共向内存申请了8byte, 为最大对齐数4(本题中三个变量对齐数分别为:1,1,4)的整数倍。
 //所以该结构体的最终大小为8
 
int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

运行结果:
自定义类型:结构体进阶学习分享,C语言学习分享,学习,c语言,c++,开发语言
—————————————————————————————————————

笔试题二:

struct S3
{
	double d;//第一个成员变量对齐到偏移量为0处,在向后申请8字节(double大小为8)
	char c;//该成员变量对齐数为1(char为1byte,VS默认对齐数为8,取最小值)
		   //所以变量对齐到偏移量为8处,向后申请1byte
	int i;//该成员变量对齐数为4(int为4byte,VS默认对齐数为8,取最小值)
		  //所以该变量首先要对齐到偏移量为12的位置(必须对齐到4的整数倍处),在向后申请4个字节
};
//到此该结构体向内存共申请了16字节,为最大对齐数8的整数倍
//所以该结构体的最终大小为16byte

struct S4
{
	char c1;//第一个成员变量对齐到偏移量为0处,在向后申请1字节(char大小为1)
	struct S3 s3;//嵌套结构体,结构体要对齐到自己最大对齐数8(上面已经求得最大对齐数为:8)的整数倍处。即该成员变量对齐到偏移量为8出,在向后申请16字节(上面已经求出其大小16byte)
	double d;//该成员变量对齐数为8(double为8byte,VS默认对齐数为8,取最小值)
		     //上述两个变量总共为24个字节,所以该成员变量从偏移量为24(刚好为8得整数倍)开始,在向后申请8字节
};
//到此该结构体总共占据32个字节空间,为最大对齐数8(上述3个成员变量对齐数分别为:1,8,8)的整数倍。
//所以最终该结构体总大小为32byte

int main()
{
	printf("%d\n", sizeof(struct S3));
	printf("%d\n", sizeof(struct S4));
	return 0;
}

运行结果:
自定义类型:结构体进阶学习分享,C语言学习分享,学习,c语言,c++,开发语言

6.2 为什么存在内存对齐?

大部分参考资料是这么说的:

1.平台原因(移植原因)
不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处去某些特定类型的数据,否则抛出硬件异常。
2. 性能原因
结构数据(尤其是栈)应尽可能地在自然边界上对齐。
原因在于为了访问未对其的内存,处理器需要作两次内存访问;而对齐的内存仅需要一次访问。

总的来说:

结构体的内存对齐是拿空间来换时间的做法。

6.3 如何设计结构体减少空间

在设计结构体时,我们既要满足对齐,又要节省空间,如何做到呢?

我们前面已经见过这段代码:

struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	char c2; 
	int i;
}
int main()
{
	printf("%d\n", sizeof(struct S3));//12
	printf("%d\n", sizeof(struct S4));//8
	return 0;
}

S1和S2类型的成员一模一样,但是S1和S2所占据的空间大小有一些区别。我们可以得到:

  • 在设计结构体时,尽量让占用空间小的成员尽量集中在一起,从而节省空间。

1.7 修改默然对齐数

#pragma这个预处理指令可以被用来修改我们的默认对齐数。

#include <stdio.h>

#pragma pack(8)//设置默认对齐数为8
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消默认对齐数

#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消默认对齐数

int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

运行结果:
自定义类型:结构体进阶学习分享,C语言学习分享,学习,c语言,c++,开发语言
总结:

  • 结构在对齐方式不合适说的时候,我们可以自己更改默认对齐数。

8. 结构体传参

直接上代码:

#include <stdio.h>
struct S
{
	int date[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };

//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* s)
{
	printf("%d\n", s->num);
}

int main()
{
	print1(s);
	print2(&s);
	return 0;
}

上面print1和print2函数那个更好?
答案是:首选print2函数。
原因:

函数传参的时候,参数是需要压栈的,会有时间和空间上的开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。

结论:

结构体传参时,要传结构体指针。

9. 结尾

本篇博客到此就结束了。传作不易,如果对你有帮助记得三连哦!感谢您的支持!!!文章来源地址https://www.toymoban.com/news/detail-600243.html

到了这里,关于自定义类型:结构体进阶学习分享的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【进阶C语言】自定义类型:结构体,枚举,联合

    前言 作者简介: 热爱跑步的恒川 ,正在学习C/C++、Java、Python等。 本文收录于 C语言进阶 系列,本专栏主要内容为数据的存储、指针的进阶、字符串和内存函数的介绍、自定义类型结构、动态内存管理、文件操作等,持续更新! 相关专栏Python,Java等正在发展,拭目以待!

    2023年04月14日
    浏览(72)
  • 【C语言进阶】自定义类型:结构体,枚举,联合

    1.1结构体类的基础知识 结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.2结构的声明 例如描述一个学生: 1.3特殊的声明 在声明结构的时候,可以不完全的声明 上面的两个结构在声明的时候省略掉了结构体标签(tag) 当我们使用匿名结

    2024年02月07日
    浏览(43)
  • 【C语言进阶(七)】自定义类型--结构体,位段,联合

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C语言学习分享⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习更多C语言知识   🔝🔝 文章目标: 本篇文章着重给大家讲解: 结构体内存对齐的知识 并且介绍位段,联合的内容 最后对这一板块做出拓展 结构体,位段和

    2024年02月15日
    浏览(42)
  • 【C语言进阶】自定义类型之结构体,枚举和联合

    人一能之,己百之;人十能之,己千之。                             ——《中庸》   目录 一.结构体 1.结构的基础知识 2.结构体的声明 3.结构体成员的类型 4.结构体变量的定义和初始化: 5.结构体成员的访问: 6.结构体传参  7.结构体内存对齐:结构体的大小 8.为什么要

    2023年04月23日
    浏览(43)
  • C语言进阶,第4节-自定义类型详解(结构体

    1. 结构的声明 //描述一个学生 //匿名结构体类型 编译器会把上面两种声明当做完全不同的两个类型 2. 结构体自引用 //正确的自引用方式: 注意: 3. 结构体变量定义和初始化 //定义 //初始化 4. 结构体内存对齐 //结构体对齐规则 : //例 1 //例 2 //为什么存在内存对齐?( 结构体

    2024年02月16日
    浏览(39)
  • C语言学习:8、深入数据类型

    C语言中,如果需要用的整数大于int类型的最大值了怎么办? 我们知道int能表示的最大数是2147483647,最小的数是-2147483648,为什么? 因为字32位系统中,寄存器是32位的,寄存器中最高位表示符号位,0表示整数,1表示负数; 所以32位系统中int的最大值可以表示为0111 1111 1111

    2024年02月09日
    浏览(37)
  • C语言学习分享(第一次)------初识C语言

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C语言学习分享⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习更多C语言知识   🔝🔝 前有鲁迅弃医从文,今有我卷王…让我们正经一点,回归主题 首先,C语言在计算机邻域起着承上启下的作用,很多其他的计算机语言

    2023年04月22日
    浏览(64)
  • C语言学习分享(第六次)------数组

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C语言学习分享⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习更多C语言知识   🔝🔝 啊~~,很久没有更新C语言知识了,各位久等了,本篇文章在了解了数组的基本知识后, 着重于给大家实现两个小游戏:三子棋和扫雷

    2024年02月03日
    浏览(62)
  • 第六十一天学习记录:C语言进阶:C语言预处理1

    在ANSI C的任何一种实现中,存在两个不同的环境。 第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。第2种是执行环境,它用于实际执行代码。 翻译环境 ![在这里插入图片描述](https://img-blog.csdnimg.cn/04bd03e2cb554aa298fb6a8349722f89.png 上图截取自比特科技免费课程

    2024年02月07日
    浏览(47)
  • C语言学习分享(第八次)------数据的存储

    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C语言学习分享⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你学习更多C语言知识   🔝🔝 本篇文章带给大家的内容有,整型在内存中的存储,大端小端的介绍与判断,和一些练习.从这篇文章开始,我们将进入C语言进阶知识

    2024年02月07日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包