自定义类型——结构体(C语言进阶)

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

目录

一、结构的声明

1.1 一般声明

1.2 特殊声明

1.3 结构体自引用

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

 2.1 结构体变量的定义

2.2 结构体变量的初始化

三、结构体内存对齐

3.1 代码分析

3.2 结构体内存对齐的规则

 3.3 嵌套结构体的大小

3.4 存在结构体内存对齐的原因

3.5修改默认对齐数

四、结构体传参

        结构是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同类型的变量。这里小编将带着大家一起来了解学习结构体,分别从结构体的声明、自引用,结构体的定义和初始化,以及结构体内存对齐和结构体传参这些方面来学习结构体。

一、结构的声明

1.1 一般声明

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

        结构体的语法形式如上代码,struct是关键字,tag是个标签名,这个是可以自定义的,根据自己实际情况来进行定义的,紧跟着是大括号,里面是成员列表,最后大括号外面是变量列表。举个例子吧,用结构体来描述学生的相关信息。

struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}s1,s2,s3; //分号不能丢

        这里的name、age、sex和id都是成员列表。s1、s2、s3是结构体变量,当然也可以在下面重新命名,例如:

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

int main()
{
    struct Stu s1,s2,s3;
    return 0;
}

1.2 特殊声明

        在声明结构体的时候,可以不完全声明。

例如:

struct
{
	int a;
	char b;
	float c;
}x;
int main()
{
	 
	return 0;
}

        如上代码,struct后面没有标签名,只是在整个结构体后面直接命名了一个x作为结构体的变量名,这种就属于匿名结构体类型,其实这种匿名结构体类型一般情况只能用一次,在后面代码中不能再次创建结构体了。

        在此基础上,我们在创建一个成员相同结构体,以结构体指针的形式命名,如下代码,

struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}* p;
int main()
{
	p = &x;//错误写法
	return 0;
}

此时我们会认为,这两个结构体成员一模一样,然后我们用p来接收x的地址,其实这是错误的。

注意:这样编译器会发出警告,警告说p的类型和&x的类型不兼容,因为我们这两个结构体连标签都没有,编译器会认为这是两种不同结构体。这种匿名结构体类型绝大部分情况下我们不会去使用。

1.3 结构体自引用

自定义类型——结构体(C语言进阶),c语言,开发语言

         结构体的自引用就类似数据结构中的链表,为了找到下个节点,在自己的成员列表中记录自己同类型的结构体的指针作为节点。

注意:必须是下个结构体的指针(地址),不能存放下个结构体的变量名

struct Node
{
	int data;//数据域
	struct Node* Next;//指针域
};

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

 2.1 结构体变量的定义

struct Point
{
    int x;
    int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2

定义结构体变量有两种方法:

①声明类型的同时定义变量

②重新定义结构体变量

2.2 结构体变量的初始化

struct Stu
{
	char ch;
	int a;
}s1={.a=100,.ch='x'},s2={.a=500,.ch='f'};
//s1={'w',100},s2={'s',200};

如上这种声明类型和定义结构体变量的同时初始哈结构体成员变量,我们可以使用' . '操作符来进行赋值,这样可以不按照顺序来赋值,也可以直接进行赋值初始化,但需要按照顺序。

当然结构体里面也可以包含其他的结构,初始化也是同理。代码如下:

struct Stu
{
	char ch;
	int a;
}s1, s2;

struct School
{
	float d;
	struct Stu s1;
	int x;
	int arr[3];
};
int main()
{
	struct School Sn = { 3.14,{'m',666},999,{4,5,6} };
	return 0;
}

三、结构体内存对齐

3.1 代码分析

        这时我们以及掌握结构体的基本使用了,那么接下来我们来深入探讨一下结构体的一个很重要的问题,计算结构体大小,这是一个很热门的考点(结构体内存对齐)。

我们先来看一个练习吧,

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

	printf("%d\n", sizeof(struct S2));

	return 0;
}

当我们看到上面的题目时候,感觉这两个结构体的是一样的,就只是成员变量顺序换了一下罢了,我们会以为这两个结构体的大小是一样大的,char是一个字节,int是四个字节,所以会以为这两个结构体的大小都是6字节,当我们运行代码之后,我们发现事实却不是这样的。

自定义类型——结构体(C语言进阶),c语言,开发语言

第一个结构体的大小是12字节,第二个结构体的大小是8字节,为什么是这样的呢,这里就涉及到结构体内存对齐的问题。 

这里先介绍一个宏,叫做 offsetof ,这个宏可以计算结构体某个成员相较于结构体起始位置的偏移量。(如下图)

两个参数,第一个参数是结构体类型,第二个参数结构体成员,头文件<stddef.h>

自定义类型——结构体(C语言进阶),c语言,开发语言

 代码如下:

#include<stddef.h>
struct S1
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%d\n", offsetof(struct S1, c1));
	printf("%d\n", offsetof(struct S1, i));
	printf("%d\n", offsetof(struct S1, c2));

	return 0;
}

自定义类型——结构体(C语言进阶),c语言,开发语言

 我们发现,c1距离结构体起始位置的偏移量是0,i距离结构体起始位置的偏移量是4,c2距离结构体起始位置的偏移量是8,如下图所示

自定义类型——结构体(C语言进阶),c语言,开发语言

但是这样算下来,应该是9个字节呀,为什么显示的是12个字节呢?其实本质上,在最后又浪费了3个字节,使得最后是12个字节。接下来,我们来研究一下为什么是这样呢?

3.2 结构体内存对齐的规则

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

利用这些规则,来解读一下上面的疑惑,第一个成员c1放在了结构体的第一个空间的位置,也就是与结构体偏移量为0的地址,然后从第二个成员开始,往后每一个成员都要对齐到某个对齐数的整数倍处。这里小编使用的是VS,默认对齐数是8,针对我们这个代码来说,第二个成员变量i的自身大小为4,编译器的默认对齐数是8,所以i的对齐数为4,所以所放的位置应该对齐到4的倍数处,此时会发现前面有3个字节的空间浪费掉了,再看第三个成员变量,通过计算c2的对齐数1,这时直接向后放就可以了,那么这里已经使用9个字节了,怎么算的12呢,这时就要利用第三个规则了,第一个变量对齐数是1,第二个变量的对齐数是4,第三个变量的对齐数1,所以最大对齐数是4,结构体总大小为最大对齐数的整数倍,由此可以得出,结构体的总大小为12。如下图所示,

自定义类型——结构体(C语言进阶),c语言,开发语言自定义类型——结构体(C语言进阶),c语言,开发语言

 3.3 嵌套结构体的大小

        在上面,我们把结构体在内存对齐的规则讲完后,就把最开始的几个代码和疑惑全部解释清楚了,但是规则4我们还没有利用到,这里小编再聚一个例子来解读一下规则4.

struct S3
{
	double d;
	char c;
	int i;
};
struct S4
{
	char c1;
	struct S3 s3;
	double e;
};

看到上面代码,通过规则我们可以轻易的算出s3结构体的大小为16字节。那么结构体s4的大小为多少呢,首先c1占用第一个字节,然后成员s3,s3的最大对齐数(s3自己所有成员的对齐数的最大值)为8,自身大小为16,所以对齐数为8,所以s3要放在8的整数倍处,然后在放e,计算完发现此时使用了32个字节,最后结构体的总大小为最大对齐数,也就是8的倍数,发现32刚好是32的倍数,所以最后结构体s4的大小为32。

        好了,讲到这里,结构体内存对齐的规则已经讲清楚了,但是,为什么会存在结构体对齐这个东西呢?

3.4 存在结构体内存对齐的原因

大部分资料是这样说的。

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

3.5修改默认对齐数

        其实默认对齐数是可以修改的。这里给大家介绍一个预处理指令。

#pragma pack(4) //修改默认对齐数4

注意:①一般情况下,我们修改的默认对齐数都是2的次方数,很少出现3,5这样的默认对齐数

        ②当我们修改默认对齐数并使用完之后,一定要取消设置默认的对齐数。

举个例子来为大家说明吧。

#pragma pack(1)//讲默认对齐数改为1
struct S5
{
	char ch1;
	int x;
	char ch2;
};
#pragma pack()  //取消设置默认对齐数
int main()
{
	printf("%d\n", sizeof(struct S5));
	return 0;
}

自定义类型——结构体(C语言进阶),c语言,开发语言

 如上,当我们修改完默认对齐数之后,再利用规则就可以算出,结构体S5的大小为6。

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

四、结构体传参

        最后,我们来学习一下结构体传参。有时候我们创建一个结构体变量之后,不会直接使用它,而是作为参数,来进行传参。

①直接以结构体变量名作为参数

②以结构体的地址作为参数

如下代码:

struct S
{
	int data[1000];
	int num;
};
//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);//点操作符解引用
}
//结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);//指针通过->解引用
}
int main()
{
	struct S s = { {1,2,3,4}, 1000 };

	print1(s); //传结构体
	print2(&s); //传地址
	return 0;
}

如上代码,我们通过两种方式传参都可以实现我们的效果。那么到底哪一种更合适呢,哪一种更好呢?其实是穿结构体指针更好一些,为什么呢?

        当我们以结构体变量作为实参来传参的时候,实参传给形参的时候,我们知道,形参是实参的一块临时拷贝,它也需要准备一个很大的空间来存放拷贝过来的数据。函数传参的时候,系统会进行压栈的,这些系统消耗会比较大,浪费空间。

        当我们以指针的形式传参的时候,系统只会开辟4/8个字节,消耗会比较小,省空间。

结论:

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

也就是我们要传结构体指针。

        好了,到这里,今天要讲的结构体相关的知识就讲完了,希望这篇文章对你起到一定的帮助,如果觉得小编写的还可以的,可以一键三连(点赞,关注,收藏)哦,你们的支持是对小编极大的鼓励,谢谢!!!

自定义类型——结构体(C语言进阶),c语言,开发语言

 文章来源地址https://www.toymoban.com/news/detail-603045.html

 

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

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

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

相关文章

  • C语言进阶,第4节-自定义类型详解(结构体

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

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

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

    2023年04月23日
    浏览(34)
  • 自定义类型:结构体进阶学习分享

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

    2024年02月16日
    浏览(31)
  • 【C语言进阶:自定义类型详解】枚举

    本节重点内容: 枚举类型的定义 枚举的优点 枚举的使用 枚举:将可能的取值一一列举。 比如:一周的天数、性别、一年的月份等等可以使用枚举来将其可能出现情况列举出来。 枚举类型的基本使用: 运行结果如下:  当然,我们也可以将这些枚举常量在定义时附上初始值

    2023年04月08日
    浏览(29)
  • C语言进阶——自定义类型:枚举、联合

    🌇个人主页:_麦麦_ 📚今日名言:如果不去遍历世界,我们就不知道什么是我们精神和情感的寄托,但我们一旦遍历了世界,却发现我们再也无法回到那美好的地方去了。当我们开始寻求,我们就已经失去,而我们不开始寻求,我们根本无法知道自己身边的一切是如此可贵

    2024年01月21日
    浏览(33)
  • 【再识C进阶4】详细介绍自定义类型——结构体、枚举和联合

           在上一篇博客中,我们已经详细地学习了字符分类函数、字符转换函数和内存函数。那这一篇博客和上一篇博客的关系不是那么相连。        这一篇博客主要介绍一下 自定义类型 ,因为在解决实际问题时, 由于世界上的因素有很多,我们需要建立不同的数据

    2024年02月08日
    浏览(36)
  • 【C语言】【数据结构】自定义类型:结构体

    这是一篇对结构体的详细介绍,这篇文章对结构体声明、结构体的自引用、结构体的初始化、结构体的内存分布和对齐规则、库函数offsetof、以及进行内存对齐的原因、如何修改默认对齐数、结构体传参进行介绍和说明。                  ✨  猪巴戒 :个人主页✨      

    2024年02月05日
    浏览(25)
  • 【C语言】自定义类型:结构体、枚举、联合

    目录 前言: 一、结构体 (1)结构体的特殊声明 (2)结构体的自引用 (3)结构体嵌套初始化 (4)结构体内存对齐 (5)修改默认对齐数 (6)结构体传参 (7)位段 二、枚举 (1)枚举的定义 (2)枚举的优点 (3)枚举的使用 三、联合(共用体) (1)联合类型的定义 (

    2024年02月16日
    浏览(32)
  • C语言—自定义类型(结构体、枚举、联合)

    结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。 实例一(描述一本书): 在声明结构的时候,可以不完全的声明。 实例一: 注:匿名结构体类型创建好之后只能用一次(没有标签构不成结构体类型) 实例一: 注:在结构中包含一个类型

    2024年02月06日
    浏览(33)
  • 【C语言】结构体类型的定义与使用

    目录 一、结构体 二、结构体的定义 1.最基本的定义 2.有 typedef 的定义 3.定义即对象方式 4.定义即对象指针方式(涉及指针就会稍微复杂一点) 5. typedef结构体和结构体指针方式 三、结构体的使用         结构体是用户自定义的一种类型,这种类型里面可以声明不同的数据类

    2024年02月12日
    浏览(34)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包