当前位置: 首页 > backend >正文

【C初阶】自定义类型--结构体

目录

1. 结构体类型的声明

1.1 结构体回顾

1.2 结构的特殊声明

1.3 结构的自引用

2. 结构体的内存对齐

2.1 对齐规则

2.2 为什么要存在内存对齐?

3. 结构体传参

4. 结构体实现位段

4.1 什么是位段

4.2 位段的内存分配

4.3 位段的跨平台问题

4.4 位段的应用


1. 结构体类型的声明

1.1 结构体回顾

我们之前就或多或少的学习了结构体的相关知识。现在,我们先来回顾一下:

结构体的声明

代码块

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

比如说描述一个学生:

// 描述一个学生
struct stu
{char name[40]; // 名字int age;       // 年龄char sex[10];  // 性别char id[11];   // 学号
}; // 这里的分号不能丢

结构体变量的初始化:

// 描述一个学生
struct stu
{char name[40]; // 名字int age;       // 年龄char sex[10];  // 性别char id[11];   // 学号
}; 
int main()
{struct stu s1 = { "zhangsan",21,"man","23405010211" };struct stu s2 = { .name = "lisi",.age = 18, .id = "23405010101", .sex = "man" };return 0;
}

1.2 结构的特殊声明

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

例如:

// 匿名结构体 -- 只能使用一次
struct
{int a;char b;float c;
}x,y,z;
struct
{int a;char b;float c;
}*p;
int main()
{p = &x;return 0;
}

这个代码运行的时候是会报警告的,因为匿名结构体只能使用一次。

1.3 结构的自引用

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

struct Node
{int data;struct Node;
};

你们看段代码对不对。答案是不对的,这样子写,我们无法确定这个结构体占据多少字节,而且它会一直递归下去是死递归。那么,如何写才是对的,没错就是使用指针。

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

这样子就是正确的。

我们在书中会看到结构体的重命名。

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

这段代码相当于把结构体重命名为Node,使用起来更加方便。

2. 结构体的内存对齐

我们已经初步了解了结构体,接下来我们计算一下结构体的大小。

一个示例:

从代码码运行的结果来看,和我们的预算值不一样,这就要学习一下结构体的内存对齐。

2.1 对齐规则

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

  1. 结构体的第1个成员对齐到和结构体变量起始位置偏移量为0的地址处。
  2. 从第2个成员变量开始,都要对齐到某个对齐数的整数倍地址处。                               对齐数 = 编译器默认的一个对齐数 与 该成员变量大小的较小值。                                    VS的默认值为8     Linux中gcc没有默认对齐数,对齐数就是成员自身的大小
  3. 结构体总大小为最大对齐数的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所以最大对齐数的整数倍。

我们在举例子之前先了解一个计算偏移量的宏:offsetof

我们就计算一下上面代码中S1的各成员偏移量。

接下来我们说说这是怎么来的,根据对齐规则。

我们就可以得到结构体S1在内存中如何存储,中间的空白就相当于浪费了。

现在,我们明白了如何储存,根据对齐规则趁热打铁对S2分析作为练习。

中间空白空间仍然是被浪费了。

2.2 为什么要存在内存对齐?

1. 平台原因(移植原因)

不是所有的硬件平台都能访问任意地址上的任意数据的;某些平台只能在某些地址处取某些特定类型的数据,否则会出现异常。

2. 性能原因

数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

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

那么,在设计结构体的时候,我们急要满足对齐,又要节省空间,该怎么做?让占用空间小的成员尽量集中在一起。

3. 结构体传参

我们了解了结构体在内存中的存储和结构体的取出,现在我们来使用一下结构体。

// 结构体传参
struct S
{int data[1000];int num;
};struct S s = { {1,2,3,4},1000 };// 结构体传参
void print1(struct S t)
{printf("print1: %d\n", t.num);
}// 结构体地址传参
void print2(struct S* ps)
{printf("print2: %d\n", ps->num);
}int main()
{print1(s); // 传结构体 传值调用print2(&s);// 传地址 传址调用
}

我们在上面的代码中使用了两种传参方式,哪一种更好呢?第二种。

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

总而言之:结构体传参的时候,要传结构体的地址。

4. 结构体实现位段

结构体的知识了解完之后,我们就可以通过结构体实现位段了。

4.1 什么是位段

位段和结构体的声明类似,有两个不同:

1.位段成员必须是int、unsigned int或signed int。

2.位段的成员名后边有一个冒号和一个数字。

代码演示

struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};

A就是一个位段类型。那么它占据多大的空间呢?

会和结构体一样吗? -- 答案是不一样。

4.2 位段的内存分配

1. 位段的成员可以是int、unsigned int、signedint或者char类型

2. 位段的空间上是按照需要以4个字节或者1个字节的方式来开辟的

3. 位段设计很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

这里我就不过多介绍了。

4.3 位段的跨平台问题

1. int位段当成有符号还是无符号是不确定的

2.位段中最大数目不确定。

3. 位段中的成员在内存中从左向右还是从右向左分配不确定,标准是未定义的。

4. 空间的利用是不稳定的。

位段可以节省空间,但是不稳定因素有点多。

4.4 位段的应用

我们在知晓每个成员占据多少位的时候,我们就可以设计位段。就比如网络传输,有的传输的内容大,有的小,特别小的我们就可以使用位段。

http://www.xdnf.cn/news/18546.html

相关文章:

  • Java:对象的浅拷贝与深拷贝
  • 探索 List 的奥秘:自己动手写一个 STL List✨
  • 基于JSqlParser的SQL语句分析与处理
  • 网址账号正确,密码错误返回的状态码是多少
  • Go语言数据结构与算法-基础数据结构
  • Compose笔记(四十七)--SnackbarHost
  • Axure:有个特别实用的功能
  • 什么是AI宠物
  • [2025CVPR-目标检测方向]PointSR:用于无人机视图物体检测的自正则化点监控
  • C++的struct里面可以放函数,讨论一下C++和C关于struct的使用区别
  • leetcode算法刷题的第十六天
  • 力扣热题之技巧
  • 雷卯针对香橙派Orange Pi 3G-IoT-B开发板防雷防静电方案
  • 云原生、容器及数据中心网络相关名词记录
  • 无人机光伏巡检误检率↓79%!陌讯多模态融合算法在组件缺陷检测的落地优化
  • 为什么存入数据库的中文会变成乱码
  • 浙江龙庭翔新型建筑材料有限公司全屋定制:畅享品质生活新境界!
  • 【小沐学GIS】基于C++绘制三维数字地球Earth(osgEarth、三维瓦片地球)第十期
  • 如何使用和优化SQL Server存储过程:全面指南
  • PETR/PETRv2
  • 从 M4S 到 MP4:用 FFmpeg 轻松合并音视频文件
  • C++矩阵类设计与实现:高效、健壮的线性代数工具
  • 2025年音乐创作大模型有哪些?国内国外模型汇总以及优点分析
  • 5G物联网的现实与未来:CTO视角下的成本、风险与破局点
  • Stm32通过ESP8266 WiFi连接阿里云平台
  • Spring Boot 校验分组(Validation Groups)高级用法全指南
  • 从0到1:数据库进阶之路,解锁SQL与架构的奥秘
  • 32位内部数据通路是什么?
  • 基于llama.cpp的量化版reranker模型调用示例
  • 【golang】制作linux环境+golang的Dockerfile | 如何下载golang镜像源