C语言 之 自定义类型:结构体、结构体内存对齐、修改默认对齐参数 详细说明 可以来看看哟

news2024/9/23 23:34:52

结构体类型的声明

结构体的声明

struct tag
{
 member-list;  //结构体中的成员,可以有多个
}variable-list;  //这里是直接创建结构体的变量,但是不一定要在这里声明变量
//不能把后面这个 ; 省略了

例如结构体用于描述一个学生:

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

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

结构体变量的创建
结构体变量的创建有以下几种方式,都是可以的

struct Book
{
	char name[20];
	char author[20];
	float price;
	char id[13];
}b1,b2; //全局变量  第一种方式
 
struct Book b3;  //全局变量  第二种方式

int main()
{
	struct Book b4;  //局部变量  第三种方式

	return 0;
}

结构体变量的初始化

struct Stu
{
	char name[20];//名字
	int age;//年龄
	char sex[5];//性别
	char id[20];//学号
}s3 = {"王五",19,"男","20240825003"};

int main()
{
	//按照结构体 成员的顺序 初始化
	struct Stu s1 = { "张三", 20, "男", "20240825001" };
	printf("name: %s\n", s1.name);
	printf("age : %d\n", s1.age);
	printf("sex : %s\n", s1.sex);
	printf("id : %s\n", s1.id);

	//按照 指定的 顺序初始化
	struct Stu s2 = { .age = 18, .name = "李四", .id = "20240825002", .sex = "女" };  // . 是访问操作符 
	printf("name: %s\n", s2.name);  //使用访问操作符就能访问到结构体中的成员
	printf("age : %d\n", s2.age);
	printf("sex : %s\n", s2.sex);
	printf("id : %s\n", s2.id);

	printf("name: %s\n", s3.name);
	printf("age : %d\n", s3.age);
	printf("sex : %s\n", s3.sex);
	printf("id : %s\n", s3.id);

	return 0;
}

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

结构体的特殊声明

在声明结构的时候,可以不完全的声明。
如下:
这叫作匿名结构体 可以直接创建该结构体的对象并初始化,省略tag

struct     //省略tag
{
	char a;
	int i;
	float b;
} s = {'f',1,3.14};

int main()
{
	printf("%c,%d,%f\n", s.a, s.i, s.b);

	return 0;
}

但是如果存在两个匿名结构体呢?

struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}a[20], * p;

如果是这样的话,就是非法的

警告:
1.编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。
2.匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。

结构体的自引用

在结构中包含⼀个类型为该结构本⾝的成员是否可以?
比如,以下创建链表的结点的代码

struct Node
{
 int data;
 struct Node next;  //类型为该结构体本身
};

这是不行的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的大小就会无穷的大,是不合理的

正确的自引用方式:

struct Node
{
 int data;
 struct Node* next;  //声明一个该类型的指针
};

typedef是一个关键字,类似define,typedef能够进行重命名
比如 typedef int DataType 就是将int命名为DataType,之后定义变量
DataType a 就相当于 int a

当我们使用typedef时

typedef struct
{
 //将这个匿名结构体重命名为Node 这是结构体使用typedef的方式
 int data;
 Node* next;
}Node;

这个也是不行的,因为Node是对前面的匿名结构体类型的重命名产生的,但是在匿名结构体内部提前使用Node类型来创建成员变量,这是不行的。

正确的应该是这样

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

结构体内存对齐

对齐规则

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

例如下面这个例子:

#include<stdio.h>
struct S
{
	char c1;  //1
	int i;  //4
	char c2;  //1
};

int main()
{
	struct S s = { 0 };
	printf("%zd\n", sizeof(s));

	return 0;
}

如果你觉得它的大小是6,那就大错特错了

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

那为什么等于12,我们来看看下图
在这里插入图片描述

再来看看这个例子

#include<stdio.h>
struct S
{
	char c1;  //1
	char c2;  //1
	int i;  //4
};

int main()
{
	struct S s = { 0 };
	printf("%zd\n", sizeof(s));

	return 0;
}

虽然结构体成员与上一个例子的成员相同,但是输出结果呢?
输出结果:
在这里插入图片描述在这里插入图片描述

所以我们由此可以知道,一个结构体中有相同的结构体成员,但是由于对齐的问题,所以会因为成员变量的顺序不同而导致结构体的大小不同

那么对于嵌套的结构体呢?

#include<stdio.h>
//结构体嵌套问题
struct S3
{
	double d;
	char c;
	int i;
};
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

int main()
{
	printf("%d\n", sizeof(struct S4));
	return 0;
}

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

为什么存在内存对齐

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

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

修改默认对齐参数

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

结构体传参

我们知道,我们在传参的时候有两种方式,一种是传值,另一种是传参
例子:

struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };

//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}

//结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);
}

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

像这种情况,上面的 print1 和 print2 函数哪个好些?
我们应该选择哪个呢?

我们应该首选print2函数。

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

所以建议结构体传参的时候,传结构体的地址。

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

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

相关文章

MySQL内部临时表(Using temporary)案例详解及优化解决方法

目录 前言 一.场景案例 二、什么是内部临时表&#xff1f; 三、哪些场景会使用内部临时表&#xff1f; 四、内部临时表如何存储&#xff1f; 1&#xff09;使用内存 2&#xff09;先使用内存&#xff0c;再转化成磁盘文件 3&#xff09;直接使用磁盘文件 五、如何优化…

Stable Diffusion绘画 | ControlNet应用-IP-Adapter:一致性角色就这么简单

IP-Adapter 更新了全新的模型—FaceID plus V2 版本&#xff0c;同时还支持 SDXL 模型。 FaceID plus V2 版本的优点&#xff1a; 解决任务一致性 一张图生成相似角色 其中&#xff0c;两个 Lora文件 放置在&#xff1a;SD安装目录\models\Lora 两个 bin文件 放置在&#x…

三、IIC 总线协议——1、IIC总线协议介绍

IIC总线协议介绍 1、IIC介绍&#xff1a; Inter Integrated Circuit&#xff0c;同步、串行、半双工通信总线。 2、IIC总线结构图 ① 由时钟线SCL和数据线SDA组成&#xff0c;并且都接上拉电阻&#xff0c;确保总线空闲状态为高电平。 ②总线支持多设备连接&#xff0c;允许…

SSM电动车智能充电桩管理系统 项目源码24481

摘 要 随着社会对环保和可持续发展的关注不断增加&#xff0c;电动车作为清洁能源交通工具受到了广泛关注和推广。然而&#xff0c;电动车充电设施的建设和管理面临着诸多挑战&#xff0c;如充电效率低下、管理繁琐等问题。为解决这些问题&#xff0c;本研究设计开发了一款电…

揭秘无线领夹麦克风五大行业隐秘:音质失真、隐私泄露需警惕!

​无线领夹麦克风是演讲、教学、直播等场合的得力助手&#xff0c;然而市场上品牌众多&#xff0c;产品质量参差不齐&#xff0c;安全隐患层出不穷。作为一名音频设备评测师&#xff0c;我近期入手了多款无线领夹麦克风进行测评&#xff0c;下面就来为大家揭秘无线领夹麦克风行…

Jupyter Notebook详细教程

1、Ipython介绍 介绍 科学计算标准工具集的组成部分 IPython是一个免费、开源的项目&#xff0c;支持Linux、Unix、Mac OS X和Windows平台&#xff0c;其官方网址&#xff1a;Jupyter and the future of IPython — IPython IPython中包括各种组件&#xff0c;其中的两个主要…

无盘设计及其在Allegro中的具体操作

无盘设计的好处有两点&#xff1a; 去掉焊环后&#xff0c;增加了孔与线或是其它孔的间距; 去掉焊环后&#xff0c;铜皮避让的面积更少了&#xff0c;增加了铺铜平面的完整性。 一、确定通孔/过孔焊盘支持去除焊环 只有在封装中勾选了Suppress unconnected internal pads; leg…

AI大模型,互联网的中年革命?人才抢夺白热化,平均工资水平惊呆了……

写在前面 在腾讯股东大会上&#xff0c;CEO马化腾深刻指出&#xff0c;人工智能&#xff08;AI&#xff09;并非仅仅是互联网领域十年一遇的机遇&#xff0c;而是一个具有深远影响的、堪比电力发明的工业革命级别的重大机遇。 本文将包括&#xff1a; 1- 行业概览 2- 大模型…

C++必修:bitset的用法与实现

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. 位图的引入 首先我们来看一道面试题&#xff1a; 给40亿个不重复的无符号整数…

乌龙?揭露2024国自然被刷真相!

8月24日&#xff0c;国自然正式放榜&#xff0c;并且申请项目批准资助通知、不予资助通知以及专家评审意见也已发送到各位申请人手里。 中了的人兴奋庆祝&#xff0c;没中的人气愤懊恼&#xff0c;一遍又一遍的看着评审意见&#xff0c;甚至会质疑一些评审的意见有些 “不合理…

The Power of Scale for Parameter-Efficient Prompt Tuning

系列论文研读目录 文章目录 系列论文研读目录论文题目含义Abstract1 Introduction2 Prompt Tuning5.6.7.8.9.10. 论文链接 论文题目含义 刻度在参数高效快速调优中的作用 Abstract In this work, we explore “prompt tuning,” a simple yet effective mechanism for lear…

(四)Kafka离线安装 - Kafka下载及安装

Kafka官方下载地址&#xff1a;Apache Kafka 这时候下载安装版本。 我这里的安装目录在 /usr/local/ cd /usr/local/# 创建目录 mkdir kafka cd kafka mkdir kafka_log 把下载的压缩包&#xff0c;放入到/usr/local/kafka/目录下&#xff0c;解压。 # 解压 tar -zxvf kafka…

PDF招生简章如何转二维码?

​随着科技的不断发展&#xff0c;招生报名方式也在不断创新。如今&#xff0c;许多学校和企业都采用PDF招生简章来宣传招生。然而&#xff0c;传统的纸质招生简章存在携带不便、易损坏等问题。为了解决这些问题&#xff0c;将PDF招生简章转换为二维码成为了一种趋势。那你知道…

Laravel邮件发送功能的实现的方法和技巧?

Laravel邮件发信功能如何配置&#xff1f;怎么使用Laravel发信&#xff1f; 在现代Web开发中&#xff0c;邮件发送功能是不可或缺的一部分。Laravel框架以其优雅的语法和强大的功能&#xff0c;成为了许多开发者的首选。AokSend将深入探讨如何在Laravel中实现邮件发送功能&…

让甲方看得见服务器资源降本增效-软件开发不仅考虑开发成本也要重视长期的运维成本

这几天有几个开发者朋友问&#xff0c;用Go语言开发后端真能降低服务器成本吗&#xff1f;本文想分享是低成本一种解决方案&#xff0c;我们不讨论谁是世界上最好的开发语言&#xff0c;所以开发者朋友看到对比语言就不要去挣个高低。GoFly社区今天给大家分享我们这几年用下来真…

餐饮点餐外卖到店小程序系统管理

餐饮业主要以到店就餐和外卖方式/部分细分业快递配送、团餐等满足客户购餐消费需要&#xff0c;互联网时代&#xff0c;尤其是年轻人无论进店与否都追求快捷方便&#xff0c;商家也要提高自身服务效率。 制作餐饮外卖配送/到店/扫码点餐小程序并可在后台开启设置扫码点餐、到店…

黑神话悟空 PC端配置需求详解:如何为不同游戏体验选择合适的配置?

《黑神话&#xff1a;悟空》是一款备受期待的动作角色扮演游戏&#xff0c;由游戏科学&#xff08;Game Science&#xff09;开发&#xff0c;基于《西游记》改编。随着游戏的发布&#xff0c;许多玩家都在关心一件事&#xff1a;我的电脑能带动这款游戏吗&#xff1f;本文将详…

七年老玩家《王者荣耀》分析四:【更新与维护以及防沉迷系统】

目录 更新与维护 王者荣耀日常例行不停机维护的具体时间和内容是什么&#xff1f; 王者荣耀停机维护的历史案例及其对玩家体验的影响如何&#xff1f; 王者荣耀版本更新维护中&#xff0c;T0级英雄调整的标准和流程是什么&#xff1f; 王者荣耀大版本更新的准备工作包括哪些…

vscode链接到远程

点击左下角的绿色按钮选择远程链接成功后左下角会显示远程的名字点击打开文件夹&#xff0c;选择目录进行目录进行编辑

NSSCTF练习记录:[AFCTF 2018]BASE

题目&#xff1a; 根据题目&#xff0c;应为base家族解码&#xff0c;用工具套娃解就可以了