c语言的基础知识之结构体
创始人
2025-05-31 06:59:40
0

目录

  • 前言
  • 结构体
    • 结构的自引用
    • typedef函数
    • 结构体内存对齐
    • 修改默认对齐数
  • 位段
    • 什么是位段
    • 位段的内存分配
    • 位段的跨平台问题
    • 位段的意义以及应用
  • 枚举
    • 枚举常量的赋值
    • 枚举的优点
  • 总结

前言

在这里插入图片描述

欢迎来到戴佳伟的小课堂,那今天我们讲啥呢?
问得好,我们今天要讲一个未来数据结构中各种表中最常用的结构–详细讲解结构体,我们也会学枚举,通过用枚举写出星期表,也会引出一种算法,既然说了这么多,那就跟着我往下,你会收益匪浅。
//空间是如何开辟的?

结构体

结构体在我们学习数据结构与完成一些基础项目中使用的很多
结构体结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
而他的一般形式也是:

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

这也是数据结构基本格式,
在这里插入图片描述
如何使用?

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

我们在main函数里写出

struct Stu s = { "yangjie", 25, '男',12345 };//对结构体变量的定义和初始化

在struct中强调一一对应的关系。

结构的自引用

我们知道链式结构的构成,一个结构连着一个结构,形成链式结构,那我们思考的是,我能在链式表自己引用自己。这也是本文的第一个问题

在这里插入图片描述

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

为什么错误呢,这个大家应该可以想象,第三个成员c是个A类型,c的第三个成员也是个A类型,那么会一直有pp.c.c.c.c.c.c.c……,此结构体的大小没有结束,那么肯定是错误的,编译的时候肯定通不过。那么怎么能是引用自身呢,这就要看指针的功能了。
欸 我们就引出了新的概念 用指针代替直接引用达到读取到下一个结构体的功能。

正确的写法:

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

typedef函数

C语言允许用户使用 typedef 关键字来定义自己习惯的数据类型名称,来替代系统默认的基本类型名称、数组类型名称、指针类型名称与用户自定义的结构型名称、共用型名称、枚举型名称等。一旦用户在程序中定义了自己的数据类型名称,就可以在该程序中用自己的数据类型名称来定义变量的类型、数组的类型、指针变量的类型与函数的类型等。
那我们就知道不一定一直要使用struct 类型 我们可以使用Node代替struct,代码如下:

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

结构体内存对齐

我们已经掌握了结构体的基本使用了。
现在我们深入讨论一个问题:计算结构体的大小。
这也是一个特别热门的考点: 结构体内存对齐

为了让你们有更好的理解这个意思,我先出个题,大家思考下:
在这里插入图片描述

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

大家认为答案是多少,6? 8? 12?

在这里插入图片描述
这时候大家会提出疑问为啥是这个答案,这时候我要提出规则了

首先得掌握结构体的对齐规则:

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

大家可能看不懂,简单来说第一位正常占进去,后面的要按照对齐数比较后进行排位,很有意思的是内存有脾气,他只放在他的位数倍数上
在这里插入图片描述
按照这种讲解我们发现这种题目的规律以及使用规则去练习sizeof(struct)的使用。

为什么存在内存对齐?

大部分的参考资料都是如是说的:

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

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

我们再写结构体的时候尽量将小的集中,尽量不必要的内存损失。

修改默认对齐数

之前我们见过了 #pragma 这个预处理指令,这里我们再次使用,可以改变我们的默认对齐数,代码如下:

#include 
#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));

在这里插入图片描述

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

位段

什么是位段

位段(bit-field)是以位为单位来定义结构体(或联合体)中的成员变量所占的空间。含有位段的结构体(联合体)称为位段结构。采用位段结构既能够节省空间,又方便于操作。
位段的声明和结构是类似的,有两个不同:

1)位段的成员必须是 int、unsigned int 或signed int 。 2)位段的成员名后边有一个冒号和一个数字。

struct node
{unsigned int a:4;     //位段a,占4位unsigned int  :0;     //无名位段,占0位unsigned int b:4;     //位段b,占4位int c:32;             //位段c,占32位int  :6;              //无名位段,占6位
};

我们思考下sizeof(struct node)的大小

位段的内存分配

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

根据上面的代码我们可以看看大小

//一个例子
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
struct S s = {0};
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
//空间是如何开辟的?

在这里插入图片描述

位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。 总结: 跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

位段的意义以及应用

在这里插入图片描述
在网络协议中运用广泛,后面我会在网络继续讲解此知识。
我尽量在网上查找资料,解答大家问题。

枚举

我们在生活总是遇见暴力枚举解题的概念,那我们今天就介绍枚举。

枚举顾名思义就是一一列举。 把可能的取值一一列举。 比如我们现实生活中: 一周的星期一到星期日是有限的7天,可以一一列举

enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
enum Color//颜色
{
RED,
GREEN,
BLUE
};

以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。
{}中的内容是枚举类型的可能取值,也叫 枚举常量 。

枚举常量的赋值

enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};

这是最基础的枚举常量的赋值。
现在我们在讲下枚举常量的使用

enum Day//星期
{Mon=1,Tues,Wed,Thur,};
#include
int main()
{printf("%d", Tues);
}

我们会发现Tues是2,其实我们也发现enum类型为int 类型大小

所以枚举规则则是:这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值

枚举的优点

为什么使用枚举

我们可以使用 #define 定义常量,为什么非要使用枚举?

枚举的优点:

  1. 增加代码的可读性和可维护性
  2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
  3. 防止了命名污染(封装)
  4. 便于调试
  5. 使用方便,一次可以定义多个常量

总结

时间过的好快,一瞬间写到了结构体的相关内容,一瞬间又老了一岁,希望看完我的文章有所收获,感谢大家的支持,我还会继续创作的

在这里插入图片描述

相关内容

热门资讯

分别的签名 与分别有关的签名 ... 人生总是在兜兜转转,有聚便会有散,在签名里,有哪些句子是与分别有关的呢?下面请欣赏太阳教育网小编为大...
个性签名骂朋友的话语 个性签名...  阁下长得真是天生励志!比较有个性一点儿的骂朋友的话语还有哪些呢?下面是太阳教育网小编为你搜集的个性...
个性签名的话 QQ个性签名的话... 得不到的,会一直被渴望着,而失去的,也总是被铭记的。适合用来做qq个性签名的话语还有哪些呢?下面是太...
最新或2023(历届)最新唯美... 喜欢唯美古风的你,知道在最新或2023(历届)出现了哪些与唯美古风有关的签名吗?下面请欣赏太阳教育网...
兄弟分别的签名 表达兄弟分别的... 兄弟是我们在这一生中难得的朋友,知己,当我们与兄弟分离的时候,如何用一句签名来表达我们当时的心情?下...
java基础:Java中vol... 一、内存模型的相关概念 大家都知道,计算机在执行程序时,每条指令都是在C...
Dubbo的独门绝技,SPI实... 文章目录前言普通SPI实现原理实例化扩展点源码分析扩展点加载流程分析LoadingStrategy分...
心灰意冷伤感说说短语 撕心裂肺...   记住我能把你宠上天也能杀你不眨眼  像被人在身后狠狠刺中了心 我心碎的声音感觉很清晰  爱是想你...
格言的话 励志经典格言 励志格... 只有创造,才是真正的享受,只有拚搏,才是充实的生活。这样的激励人心的励志格言还有哪些呢?下面是太阳教...
积极向上个性微信签名 描述男人...  1. 再长的路,一步步也能走完,再短的路,不迈开双脚也无法到达。  2. 人生伟业的建立,不在能知...
个性的话很 有个性的话 100... 只要不下流,我们就是主流!这样的比较具有个性的话语还有哪些呢?下面是太阳教育网小编为你分享的很有个性...
个性签名送给贱人的话 贱人闭嘴... 别人要开飞机去撞双子星才行,而你只要跳伞就有同样的威力。骂贱人时说的很有个性的话语还有哪些呢?下面是...
java LinkedHash... 目录 一、前言 二、LinkedHashSet简介 三、LinkedHashSet的底层实现 四、L...
QT学习(二)——按钮相关接口... 2.1 按钮相关接口 QPushButton 继承于 QAbstractButton 继承于 QWi...
让人心碎的伤感说说短语 让人心...   第一眼就心动的人,要怎么做朋友。  你要是腻了就走吧放心我不赖着你。  是时候该离开那个不属于我...
心碎到窒息的说说短语 心碎了伤...   时间让我忘了你的脸,却忘不了那些爱恋。  喂,我喜欢你“大冒险又输了?”“嗯”其实他不知道这也是...
六一儿童节看望福利院儿童活动策...  “六·一”儿童节看望福利院儿童活动策划最新或2023(历届)  六月,是孩子的节日,六月,是童年的...
幽默励志个性签名最新或2023...   01、 你要先走你必须去走的路,才能去走你想走的路。  02、 我总以为悲剧的女主角可以放肆地宣...
学校5月31日世界无烟日活动策... 1,、卫生广播:  (1)全员动员:布置学校第21个世界无烟日的宣传活动计划  (2)宣传:吸烟的危...
AtCoder Beginne... 上午第二节没课浅浅vp了一下,感觉F很有质量啊,今晚加训状压DP...