【C语言】初阶结构体

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

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

👦个人主页:Weraphael
✍🏻作者简介:目前正在回炉重造C语言(2023暑假)
✈️专栏:【C语言航路】
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注😍


一、结构体的声明

1.1 结构的概念

  • 结构是一些=值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。
  • 类比数组。数组也是一些值的集合,但类型是相同的

1.2 结构的定义

结构体是用来描述复杂对象的

// tag - 是标签名,是可以根据需求改变的
struct tag //这一整串是类型
{
	//代码块里是结构体成员
	//可以是不同类型
   	member_list // 成员列表	
}list; 
//list - 全局结构体变量 
//注意:后面有分号

还可以用typedef来定义

//描述一位学生
typedef struct tag
{
	member_list // 成员列表	

}tag; //tag- 将结构体类型struct tag重新命名为tag
//分号不可缺

通过上面的模板来举个例子

假设要描述一名学生(名字、年龄、分数)

struct student //类型
{
    //结构体成员
	char name[20]; 
	int age;      
	double score;
 
}stu,stu2; 
// stu、stu2 - 也是结构体变量(全局变量)
 
int main()
{
	// 创建结构体变量(局部变量)
	// 类型 + 变量
	struct student stu1; 
	
	return 0;
}

1.3 结构成员的类型

结构的成员可以是标量、数组、指针,甚至是其他结构体。

1.4 结构体变量的初始化

struct student //类型
{
	char name[20]; 
	char sex[10];
	int age;
	double score;
 
}; 
 
int main()
{
    //结构体变量初始化
	struct student stu1 = { "小明","男",18,72.5 }; 
		
 	return 0;
}

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

1.5 结构的特殊声明

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

【匿名结构体类型(缺少标签)】

// 匿名结构体类型(不愿起名)
struct 
{
	int a;
	char b;
	double d;
}s;

注意:

  • 若要使用匿名结构体类型,在创建类型马上在后面创建变量
  • 如果两个匿名结构体中的成员变量完全一样也会被编译器当成两个完全不同的类型
    【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

1.6 结构的自引用

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

【举个例子】

假设内存中有1,2,3。它们不是连续存储的,而是随机分布的。那应该如何存储它们呢?我们可以使用链表(是一种数据结构),一个节点中存储一个数,并且当前的节点能够找到下一个节点。所以我们可以把节点定义成一个结构体

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

【错误代码】

struct Node
{
	int data;
	struct Node next;
};

上面的代码是错误的,假如说要计算结构体的大小,data是4个字节,next包含date和下一个节点,下个next又包含date和下一个节点…,这样下来会发现它的大小其实是very very大的。

【正确代码】

我们可以放一个指向下一个节点的指针,也就是结构体指针

struct Node
{
	int data; //4个字节
	struct Node* next; //4/8个字节
};

那么接下来我把代码改成这样是否也是正确的?

typedef struct Node
{
	int data;
	Node* next;
}Node;

这其实是错误的!因为代码编译是从上到下的,当来到Node* next时,struct Node还未被typedef重命名
【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

【正确代码】

typedef struct Node
{
	int data;
	struct Node* next;
}Node;

二、结构体成员的访问

往期博客链接:点击传送

2.1 变量 . 成员

#include <stdio.h>
 
struct student //类型
{
	char name[20]; 
	int age;
	char sex[10];
	double score;
 
}; 
 
int main()
{
	//结构体变量初始化
	struct student stu1 = { "小明",18,"男",95.5 };
 
	//结构体访问
	printf("名字:%s\n年龄:%d\n性别:%s\n分数:%.1lf\n", stu1.name, stu1.age, stu1.sex, stu1.score);
 
	return 0;
}

【程序结果】

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

2.2 指针变量 -> 成员

 
#include <stdio.h>
 
struct student //类型
{
	char name[20]; 
	int age;
	char sex[10];
	double score;
 
}; 
 
int main()
{
	//结构体变量初始化
	struct student stu1 = { "小明",18,"男",95.5 };
 
	struct student* pa = &stu1;
	//结构体访问
	printf("名字:%s\n年龄:%d\n性别:%s\n分数:%.1lf\n", pa->name,pa->age,pa->sex,pa->score);
 
	return 0;
}

【程序结果】

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

2.3 解引用访问

#include <stdio.h>
 
struct student //类型
{
	char name[20]; 
	int age;
	char sex[10];
	double score;
 
}; 
 
int main()
{
	//结构体变量初始化
	struct student stu1 = { "小明",18,"男",95.5 };
 
	struct student* pa = &stu1;
	//结构体访问
 
	//*pa就是stu1
	printf("名字:%s\n年龄:%d\n性别:%s\n分数:%.1lf\n",(*pa).name,(*pa).age,(*pa).sex,(*pa).score);
 
	return 0;
}

【程序结果】

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

三、结构体嵌套

#include <stdio.h>
 
struct S
{
	int a;
	char c;
};
struct P
{
	double d;
	struct S s;
	double f;
};
 
int main()
{
	//100和's'是结构体s的
	struct P p = { 5.5,{100,'s'},33.3 };
	
	//只打印嵌套的结构体
	printf("%d %c\n", p.s.a, p.s.c);
	return 0;
}

【程序结果】

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

只要一步一步去访问,嵌套结构体同样也能访问。当然也可以用指针来访问,这里就不为大家演示了。

四、结构体传参

4.1 传值调用

注意:如果使用传值调用,形参的改变不影响实参。

#include <stdio.h>
struct student
{
	char name[20];
	int age;
	char sex[6];
	double score;
};
//不需要返回参数用void
void Print(struct student stu) //传结构体变量同样也能用结构体变量来接收
{
	printf("名字:%s\n年龄:%d\n性别:%s\n分数:%.1lf\n", stu.name, stu.age, stu.sex, stu.score);
}
int main()
{
	struct student stu1 = { "小明",18,"男",95.5 };
 
	//封装一个Print函数负责打印
	Print(stu1);
 
	return 0;
}

【程序结果】

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

4.2 传址调用

#include <stdio.h>
struct student
{
	char name[20];
	int age;
	char sex[6];
	double score;
};

//不需要返回参数用void
void Print(struct student* stu) //传地址需要用指针来接收
{
	printf("名字:%s\n年龄:%d\n性别:%s\n分数:%.1lf\n", (*stu).name, (*stu).age, (*stu).sex, (*stu).score);
	
	// 或者还可以使用->操作符来访问
	//printf("名字:%s\n年龄:%d\n性别:%s\n分数:%.1lf\n", stu->name, stu->age, stu->sex, stu->score);
}

int main()
{
	struct student stu1 = { "小明",18,"男",95.5 };
 
	//封装一个Print函数负责打印
	Print(&stu1);
 
	return 0;
}

【程序结果】

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

4.3 传值调用和传址调用的区别

那么大家认为传值调用好还是传址调用好呢?

答案是:传址调用

  • 因为传址调用时,形参也有自己独立的空间,把实参传递(拷贝)给形参就要消耗时间,这时传参的压力就比较大。如果是传址调用,因为一个地址的大小无非就是4/8个字节,形参用指针变量接收,压力也相对较小些。
  • 官方说法:函数传参的时候,参数是需要压栈的。 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

因此,建议大家在结构体传参时,都使用传址调用

五、结构体内存对齐(重点)

5.1 计算结构体大小

在了解结构体内存对齐之前,我们先想想 如何计算结构体的大小

#include <stdio.h>

struct Stu1
{
	char c;
	int a;
	double x;
};

struct Stu2
{
	char c;
	double x;
	int a;
};

int main()
{
	printf("Stu1:%d\n", sizeof(struct Stu1));
	printf("Stu2:%d\n", sizeof(struct Stu2));

	return 0;
}

【程序结果】

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

为什么Stu1Stu2的成员变量一样,就仅仅交换了位置,结构体大小却不一样?这就要涉及到 结构体内存对齐
🎈结构体内存对齐的规则

  1. 第一个成员在与结构体变量偏移量为0的地址处
  2. 其他成员变量要对齐到某个数字 (对齐数)的整数倍的地址处
    注:对齐数是 编译器默认对齐数与该成员类型大小的较小值。
    VS中默认对齐数为8
    Linux环境下无对齐数,对齐数就是结构体成员的自身大小
  3. 结构体总大小为所有结构体成员最大对齐数的整数倍。

现在解释开头的代码

以下解析默认是在visual studio环境下

【Stu1】

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

【Stu2】

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

5.2 嵌套结构体大小计算

#include <stdio.h>

struct Stu1
{
	char c;
	int a;
	double x;
};

struct Stu2
{
	char c;
	struct Stu1 s1;
	int a;
};

int main()
{
	printf("Stu2:%d\n", sizeof(struct Stu2));

	return 0;
}

如果结构体中嵌套了结构体成员,要将嵌套的结构体成员对齐到自己成员中最大对齐数的整数倍处,最后的最大对齐数还要包括嵌套成员的对齐数。

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

5.3 验证偏移量为0的地址处

C语言有一个宏叫offsetof,它的功能是计算一个结构体成员相较起始位置的偏移量(头文件:#include <stddef.h>

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

5.4 修改默认对齐数

在计算结果体大小中,我们提到了默认对齐数,在VS中默认对齐数为8,那能否修改默认对齐数呢?

可以用#pragma这个预处理指令

#pragma pack(需要修改的对齐数)

【例如】

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

那么接下来问题来了,怎么恢复呢?

// 恢复默认对齐数
#pragma pack()

六、实战 - 通讯录

点击跳转

七、位段

7.1 什么是位段

位段的声明和结构其实是类似的,但有两个不同:

  1. 位段的成员必须是intunsigned intsigned int,但也可以是char类型,因为char是属于整型家族的
  2. 位段的成员名后边有一个冒号和一个数字
  3. 位段里的成员一般都是同类型
  4. 位段的 其实表示二进制位

【举个例子】

#include <stdio.h>
struct segment
{
	int a : 2; //表示a只占内存的2个二进制位
	int b : 5;//表示b只占内存的5个二进制位
	int c : 10;//表示c只占内存的10个二进制位
	int d : 30;//表示d只占内存的30个二进制位
};

struct segment就是一个位段类型。

7.2 位段的大小

结构体的大小计算和位段的计算是一样的吗?答案其实是不一样的

【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记

位段的计算过程其实是这样的

  • int是4个字节,有32个比特位,a占内存2个二进制位,还剩下30个比特位
  • b占内存5个二进制位,还剩下25个比特位
  • c占内存10个二进制位,还剩下15个比特位
  • d占内存30个比特位,上一步还剩下15个比特位,假设把这15个比特位舍弃,继续向内存申请32个比特位给d用,所以还剩余2个比特位
    【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记
    从上过程中,类型在创建的时候向内存一共申请了32+32个比特位,也就8字节。也能看出位段其实比结构体更加节省空间,有多少用多少。

7.3 位段的内存分配

  1. 位段的成员可以是intunsigned intsigned int或者是char类型
  2. 位段的空间上是按照需要以4和字节(int)或者1个字节(char)的方式来开辟的
  3. 位段涉及很多不确定因素,位段是不跨平台的注重可移植的程序应该避免使用位段

【举个例子】

我们一起看看下面这一串代码在内存中是如何分配的

#include <stdio.h>
struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
}

首先先定制一些规则:

  1. 假设位段中的成员在内存中是从低位向高位分配
  2. 当第二个位段成员占内存二进制位比较大时,无法容纳前一个剩余位段时,直接把前一个剩余的舍弃
  3. 在visual studio测试
    分配过程如下:结构体初始化为000000000
  • a被赋值成10,转化为二进制:00001010,而a只占内存3个二进制位,也就是010(最高位的1被截断),分配:00000010
  • b被赋值成12,转化为二进制:00001100,b占内存4个二进制位,恰好能容纳上一个剩余位段。分配01100010
  • c被赋值成3,转化为二进制:00000011c只占内存5个二进制位,也就是:00011,然后这次的位段成员无法容纳上一个剩余位段,我们就舍弃,继续向内存申请:00000000,分配:00000011
  • d被赋值成4,转化为二进制:00000100d只占内存4个二进制位,然而上一个剩余位段还是无法容纳这次的成员位段,继续像内存申请:00000000,分配:00000100
  • 所以现在内存里为:0110010 00000011 00000100
    转化为十六进制也就是 62 03 04,我们F10调试,在内存中看看
    【C语言】初阶结构体,C语言航路,c语言,开发语言,学习,笔记
    这结果恰好就是我们分析出来的结果!

7.4 位段跨平台问题。

  1. int位段被当成有符号数还是无符号数是不确定的
  2. 位段中最大位的数目不能确定。16位机器最大16,32位机器最大32,如果int位段成员占内存27个二进制位,那么在16位机器会出现问题
  3. 位段中的成员咋子内存中从左向右分配,还是从右向左分配标准还未确定。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余位时,是舍弃剩余的位还是利用,这也是未确定的。

7.5 总结

跟结构体相比,位段可以达到同样的效果,但是位段可以很好的节省空间,但是有跨平台的问题存在文章来源地址https://www.toymoban.com/news/detail-578454.html

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

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

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

相关文章

  • 结构体-C语言(初阶)

    目录 一、结构体声明         1.1 结构概念         1.2 结构声明         1.3 结构成员的类型         1.4  结构体变量的定义和初始化 二、结构体成员的访问         2.1 结构体变量访问成员         2.2 结构体指针访问指向变量的成员 三、结构体传参         结构

    2024年02月10日
    浏览(32)
  • 初阶C语言-结构体

    🌞 “少年有梦不至于心动,更要付诸行动。” 今天我们一起学习一下结构体的相关内容! 📖 结构 是一些值的集合,这些值称为 成员变量 ,结构的每个成员可以是不同类型的变量。 📝为什么会出现结构体呢?在我们当前的一些学习中,我们学过的类型都是内置类型,例

    2024年02月11日
    浏览(35)
  • 【C语言】初阶结构体

    👦个人主页:Weraphael ✍🏻作者简介:目前正在回炉重造C语言(2023暑假) ✈️专栏:【C语言航路】 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞👍🏻 收藏 📂 加关注😍 1.1 结构的概念 结构 是一些= 值的集合 ,这些值称为 成员变量

    2024年02月16日
    浏览(30)
  • C语言——结构体初阶

      哈喽,大家好,今天我们来学习C语言中的结构体,今天主要学习初阶结构体,后期我们将继续学习结构体进阶。 目录 1.结构体类型的声明 1.1 结构体的基础知识 1.2 结构的声明  1.3 结构成员的类型 1.4 结构体变量的定义和初始化 2. 结构体成员的访问 3.结构体传参 结构是一

    2024年02月07日
    浏览(26)
  • 【初阶C语言】学会结构体

    1.结构体类型的声明 2.结构体初始化 3.结构体成员访问 4.结构体传参   前言:结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.结构的声明 结构体声明的模板:     这是一个模板的声明,得出来的结构体只是一种类型,与int char一样。

    2024年02月14日
    浏览(31)
  • C语言——自定义类型结构体_学习笔记

    结构体是一种用户自定义的数据类型,可以包含 多个不同类型的变量 。通过使用结构体,我们可以将相关联的数据组织在一起,便于管理和使用。 在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型的一类。 结构体可以包含多个不同类型的数据成员,例

    2024年02月07日
    浏览(36)
  • 【学习笔记】数据结构算法文档(类C语言)

    1.1.1 线性表的顺序存储表示 1.1.2 顺序表中基本操作的实现 1.1.2.1 初始化 1.1.2.2 取值 1.1.2.3 查找 1.1.2.4 插入 1.1.2.5 删除 1.1.2.6 计数 1.2.1 单链表的定义和表示 ★ 关于结点 1.2.2 单链表基本操作的实现 1.2.2.1 初始化 1.2.2.2 取值 1.2.2.3 查找 1.2.2.4 插入 1.2.2.5 删除 1.2.2.6 前插法创建单

    2024年02月07日
    浏览(42)
  • 『C语言初阶』第八章 -结构体

    🔥 博客主页 : 小羊失眠啦. 🔖 系列专栏 : C语言 🌥️ 每日语录 : 相信自己,比谁都棒。 ❤️ 感谢大家点赞👍收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 今天小羊又来给铁汁们分享关

    2024年02月12日
    浏览(33)
  • 『C语言初阶』第九章 -结构体

    🔥 博客主页 : 小羊失眠啦. 🔖 系列专栏 : C语言 🌥️ 每日语录 : 相信自己,比谁都棒。 ❤️ 感谢大家点赞👍收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 今天小羊又来给铁汁们分享关

    2024年02月12日
    浏览(39)
  • 『初阶数据结构 • C语言』① - 数据结构为何重要

    本文内容借鉴一本我非常喜欢的书——《数据结构与算法图解》。学习之余,我决定把这本书精彩的部分摘录出来与大家分享。 数组是计算机科学中最基本的数据结构之一。如果你用过数组,那么应该知道它就是一个含有 数据的列表。它有多种用途,适用于各种场景,下面

    2024年02月16日
    浏览(45)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包