C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】

目录

  • 1. 结构体类型的声明
    • 1.1 结构的声明
    • 1.2 结构体变量的创建和初始化
    • 1.3 结构的特殊声明---匿名结构体
    • 1.4 结构的自引用
  • 2.结构体内存对齐(重点!!)
    • 2.1 对齐规则
    • 2.2 例题讲解
    • 2.3 为什么存在内存对齐?
    • 2.4 修改默认对齐数
  • 3. 结构体传参

1. 结构体类型的声明

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

1.1 结构的声明

在这里插入图片描述
注意:

  1. 成员列表可以是不同类型的变量;
  2. 成员后一定要有分号;
  3. 花括号后也有一个分号。

例如描述一个学生:

struct Stu
{char name[20];//姓名int age;//年龄char tele[12];//电话char sex[3];//性别
};//分号不能丢

注意:上述代码没有创建变量,也没有初始化,只是声明了一个结构体类型,就像int,float一样,只是一种类型。

1.2 结构体变量的创建和初始化

接下来我们可以用结构体类型创建变量

方式1:
声明类型的同时创建,这是全局变量

struct Stu
{char name[20];//姓名int age;//年龄char tele[12];//电话char sex[3];//性别
}s1,s1;

方式2:
我们也可以在函数内部创建局部变量

# include <stdio.h>struct Stu
{char name[20];//姓名int age;//年龄char tele[12];//电话char sex[3];//性别
};int main()
{struct Stu s3;//s3是局部变量return 0;
}

我们再对结构体变量进行初始化:
方式1:直接初始化

struct Point
{int x;int y;
}p1 = { 2,3 };struct Stu
{char name[20];//姓名int age;//年龄}s1 = { "zhangsan",27 };

方式2:也可以再函数内部进行初始化

struct Stu
{char name[20];//姓名int age;//年龄
};int main()
{struct Stu s1 = { "zhangsan",24 };return 0;
}

当然,也有结构体的嵌套:

struct score
{int n;char ch;
};struct Stu
{char name[20];//姓名int age;//年龄struct score s;//嵌套一个结构体变量
};int main()
{struct Stu s1 = { "zhangsan",24 ,{45,'a'} };return 0;
}

我们也可以将其打印出来:

struct score
{int n;char ch;
};struct Stu
{char name[20];//姓名int age;//年龄struct score s;
};int main()
{struct Stu s1 = { "zhangsan",24 ,{45,'a'} };printf("%s %d %d %c\n", s1.name, s1.age, s1.s.n, s1.s.ch);return 0;
}

打印结果:
在这里插入图片描述

1.3 结构的特殊声明—匿名结构体

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

struct 
{char name[20];//姓名int age;//年龄char tele[12];//电话char sex[3];//性别
};

上面的结构在声明时省略了结构体标签,我们称为匿名结构体

注意:匿名结构体只能通过创建全局变量使用一次!!

# include <stdio.h>struct 
{char name[20];//姓名int age;//年龄char tele[12];//电话char sex[3];//性别
}s1;int main()
{return 0;
}

1.4 结构的自引用

在结构体中包含一个类型为该结构体本身的成员是否可以呢?比如,定义一个链表的结点:

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

上述代码正确吗?如果正确,那么sizeof(struct Node)是多少呢?
仔细分析,其实是不行的,因为⼀个结构体中再包含⼀个类型的结构体变量,这样结构体变量的大小就会无穷的大,是不合理的。

正确的自引用方式:

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

这样我们就把一个结点分成两部分,一部分存放数据,叫数据域,另一部分存放下一个结点的地址,叫指针域。

在结构体自引用使用的过程中,夹杂了 typedef 对匿名结体类型重命名,也容易引入问题,看看下面的代码,可行吗?

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

这种写法语法是不支持的。
因为Node是对前面的匿名结构体类型的重命名产生的,但是在匿名结构体内部提前使用Node类型来创建成员变量,这就产生了"先有鸡还是先有蛋"的问题,这是不行的。

解决方案如下:定义结构体不要使用匿名结构体了

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

2.结构体内存对齐(重点!!)

上面我们了解了结构体的基本使用。
接下来我们再讨论一个问题 :计算结构体的大小。

2.1 对齐规则

首先要知道结构体的对齐规则:

  1. 第一个成员在结构体变量偏移量为0的地址处。

  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    注意:
    (1) 对齐数=编译器默认的对齐数与该成员大小的较小值
    (1)VS中默认值为8(注意:其他编译器是没有默认对齐数的,对齐数就是自身大小。)

  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍

  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐的整数倍处。
    结构体的整体大小就是所有最大对齐数的(含嵌套结构体的对齐数)的整数倍。

2.2 例题讲解

例1:

#include <stdio.h>struct S1
{char c1;int i;char c2;
};int main()
{struct S1 s1;printf("%d\n", sizeof(struct S1));return 0;
}

输出结果:
在这里插入图片描述

画图解释:
在这里插入图片描述

例2:

#include <stdio.h>struct S2
{char c1;char c2;int i;
};int main()
{struct S2 s1;printf("%d\n", sizeof(struct S2));return 0;
}

输出结果:
在这里插入图片描述

画图解释:
在这里插入图片描述
例3:

#include <stdio.h>struct S3
{double d;char c;int i;
};int main()
{struct S3 s3;printf("%d\n", sizeof(struct S3));return 0;
}

输出结果:
在这里插入图片描述

画图解释:
在这里插入图片描述
例4:结构体嵌套问题

#include <stdio.h>struct S4
{char c1;struct S3 s3;double d;
};int main()
{struct S4 s4;printf("%d\n", sizeof(struct S4));return 0;
}

输出结果:
在这里插入图片描述

画图解释:
在这里插入图片描述

2.3 为什么存在内存对齐?

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

  1. 平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用⼀个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

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

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

让占用空间小的成员尽量集中在⼀起
如例1和例2,s1和s2的成员一模一样,但两者所占空间大小却不同。

2.4 修改默认对齐数

  • #pragma : 预处理指令,可以改变编译器的默认对齐数。
  • 使用时要引用头文件<stddef.h>
  • 注意:一般修改的对齐数都为2的n次方。不会修改为1,3,5……或负数。

例如还是用例1来说明:

#include <stdio.h>
#include <stddef.h>#pragma pack(1)//设置默认对齐数为1
struct S1
{char c1;int i;char c2;
};
#pragma()//取消设置的对齐数,还原为默认int main()
{printf("%d\n", sizeof(struct S1));
}

输出结果:
在这里插入图片描述
修改之前大小是12个字节,修改后变成了6字节。

3. 结构体传参

方式1:传值调用。访问结构体时用点(.)操作符。

#include <stdio.h>struct S
{int data[1000];int num;
};void print1(struct S ss)
{int i = 0;for (i = 0; i < 3; i++){printf("%d ", ss.data[i]);}printf("%d\n", ss.num);
}int main()
{struct S s = { {1,2,3},200 };print1(s);  //传值调用,直接传变量名return 0;
}

方式2:传址调用。访问结构体时用箭头(->)操作符。

#include <stdio.h>struct S
{int data[1000];int num;
};void print2(const struct S* ps)
{int i = 0;for (i = 0; i < 3; i++){printf("%d ", ps->data[i]);}printf("%d\n", ps->num);
}int main()
{struct S s = { {1,2,3},200 };print2(&s); //传址调用,传变量名的地址return 0;

输出结果都为:
在这里插入图片描述

上面的两种方式哪个更好呢?
答案是:传址调用更好。

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

结论:
结构体传参的时候,要传结构体的地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/696440.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenGauss数据库本地搭建并结合内网穿透实现远程访问

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试 前言 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合…

云渲染农场服务给力吗?全天候不间断服务如何保障?

近年来&#xff0c;云渲染农场以其强大的计算能力和高效的渲染速度而闻名&#xff0c;为各行各业的视觉创作提供了不可或缺的支持。但是&#xff0c;人们普遍关心的是&#xff0c;云渲染农场服务是否真的给力&#xff1f;全天候的服务又是如何保障呢&#xff1f; 实际上&#…

用Python插入页码到PDF文档

页码是许多类型文件中的重要内容&#xff0c;它能方便读者在文档中的导航。在创建PDF文档时&#xff0c;添加页码对于组织和引用内容特别有用。在本文中&#xff0c;我们将探讨如何利用Python程序高效地插入页码到PDF文档中&#xff0c;简化工作流程并创建出精美、结构合理的PD…

【JavaEE】_synchronized关键字——监视器锁monitor lock

目录 1. synchronized的特性 2. synchronized的使用 3. Java标准库中的线程安全类 1. synchronized的特性 &#xff08;1&#xff09;互斥&#xff1a; 前文已经介绍&#xff0c;某个线程执行到某个对象的synchronized中时&#xff0c;其他线程如果也执行到同一个对象&…

2024 Sora来了!“手机Agent智能体”也来了!

近日&#xff0c;Open AI发布了能够根据文本生成超现实视频的工具Sora&#xff0c;多款震撼视频引爆科技圈刷屏&#xff0c;热度持续发酵占据AI领域话题中心&#xff0c;被认为是AGI实现过程里的重大里程碑事件。新一轮的人工智能浪潮给人类未来的生产和生活方式带来巨大而深远…

VPN | 世界那么大,我想“魔法”上网看看,可以吗?

Hi&#xff0c;大家好&#xff0c;我是半亩花海。世界那么大&#xff0c;我想“魔法”上网看看&#xff0c;可以吗&#xff1f;随着网络的发展与普及&#xff0c;畅游网络世界已成为人们生活的一部分。它给我们开拓了视野&#xff0c;增长了见闻&#xff0c;丰富了知识&#xf…

FISCO BCOS(二)———配置及使用控制台

一、前言 FISCO BCOS是由金融区块链合作联盟&#xff08;深圳&#xff09;与微众银行共同发起的开源区块链项目&#xff0c;支持多链多账本&#xff0c;满足金融行业复杂业务需求。本文将介绍如何在Ubuntu操作系统上使用Linux命令配置FISCO BCOS的控制台并进行get/set操作。 目…

React 模态框的设计(一)拖动组件的设计

春节终结束了&#xff0c;忙得我头疼。终于有时间弄自己的东西了。今天来写一个关于拖动的实例讲解。先看效果&#xff1a; 这是一个简单的组件设计&#xff0c;如果用原生的js设计就很简单&#xff0c;但在React中有些事件必须要多考虑一些。这是一个系列的文章&#xff0c;…

YOLOv9来咧!

文章目录 论文:主要内容一、提出使用PGI&#xff08;Programmable Gradient Information&#xff0c;可编程梯度信息&#xff09;来解决信息瓶颈问题和深度监督机制不适合轻量级神经网络的问题。二、设计了GELAN&#xff08;Generalized ELAN &#xff0c;广义ELAN&#xff09;…

LLM 模型融合实践指南:低成本构建高性能语言模型

编者按&#xff1a;随着大语言模型技术的快速发展&#xff0c;模型融合成为一种低成本但高性能的模型构建新途径。本文作者 Maxime Labonne 利用 mergekit 库探索了四种模型融合方法&#xff1a;SLERP、TIES、DARE和passthrough。通过配置示例和案例分析&#xff0c;作者详细阐…

Ansible playbook 剧本部署WEB NFS rsync sersync(及时监控)架构

ansible playbook剧本介绍&#xff1a; playbook 是ansible用于配置&#xff0c;部署和管理被节点的剧本 由一个或多个模块组成&#xff0c;完成统一的目的&#xff0c;实现自动化操作 剧本编写需遵循yaml语法 yaml的三要素&#xff1a; 缩进&#xff1a;两个字符&#xff0c;默…

【Vue3】toRefs和toRef在reactive中的一些应用

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

C++模板->模板的概念、函数模板基本语法、函数模板注意事项、普通函数与函数模板区别、普通函数与函数模板调用规则、模板的局限性

#include<iostream> using namespace std; //交换两个整型函数 void swapInt(int& a, int& b) { int temp a; a b; b temp; } //交换两个浮点型函数 void swapDouble(double& a, double& b) { double temp a; a b; b te…

MATLAB中gtext函数用法

目录 语法 说明 示例 使用鼠标将文本添加到图窗 指定字体大小和颜色 在创建后修改文本 gtext函数的功能是使用鼠标将文本添加到图窗。 语法 gtext(str) gtext(str,Name,Value) t gtext(___) 说明 gtext(str) 在使用鼠标选择的位置插入文本 str。当将鼠标指针悬停在图…

3个wordpress中文企业主题模板

农业畜牧养殖wordpress主题 简洁大气的农业畜牧养殖wordpress主题&#xff0c;农业农村现代化&#xff0c;离不开新农人、新技术。 https://www.jianzhanpress.com/?p3051 老年公寓wordpress主题 浅绿色简洁实用的老年公寓wordpress主题&#xff0c;适合做养老业务的老年公…

高标准农田仪器设备

在当今社会中&#xff0c;农业已经逐渐走向了一条科技化、智能化的道路。高标准农田建设成为了现代化农业发展的一个重要方向。为了更好地提高农产品的产量和品质&#xff0c;科技人员们不断地在农田设备上进行创造性的改进与升级&#xff0c;以达到更加高效、节能、环保、智能…

【C语言】注释

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;C语言 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&…

【国际化】用JQuery-i18next的国际化demo,引入json

参考&#xff1a; 使用 i18next 的 jQuery 国际化 &#xff08;i18n&#xff09; 渐进式指南 (locize.com) i18next-http-backend/example/jquery/index.html at master i18next/i18next-http-backend (github.com) 文档 可能需要解决一下跨域问题&#xff0c;因为浏览器读取本…

Unity学习之Unity中的MVC思想

文章目录 1 前言2 MVC的基本概念3 不使用MVC思想制作UI逻辑3.1 拼面板3.2 面板脚本3.3 角色面板逻辑3.4 角色升级 4 使用MVC思想制作UI逻辑4.1 Model数据脚本4.2 View界面脚本4.2.1 MainView主界面4.2.2 RoleView 角色面板界面 4.3 Controller业务逻辑脚本4.3.1 MainController…

phaseDNN文章解读

文章DOI: https://doi.org/10.48550/arXiv.1905.01389 作者是 Southern Methodist University 的Wei Cai 教授 A Parallel Phase Shift Deep Neural Network for Adaptive Wideband Learning 一种并行移相深度神经网络来自适应学习宽带频率信号 20190514 核心思想&#xff1a;…