c语言->浅学结构体

系列文章目录

文章目录


前言

✅作者简介:大家好,我是橘橙黄又青,一个想要与大家共同进步的男人😉😉

🍎个人主页:橘橙黄又青_C语言,函数,指针-CSDN博客

目的:学习结构体基础内容,联合体的内容和与结构体的区别,枚举类型的理解。

1. 结构体类型的声明

struct tag
{member-list;
}variable-list;
例如描述⼀个学⽣:
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}; //分号不能丢

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

#include <stdio.h>
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
};
int main()
{//按照结构体成员的顺序初始化struct Stu s = { "张三", 20, "男", "20230818001" };printf("name: %s\n", s.name);printf("age : %d\n", s.age);printf("sex : %s\n", s.sex);printf("id : %s\n", s.id);return 0;
}

也可以按照指定循序初始化

     struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .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);

1.2 结构的特殊声明

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

//匿名结构体类型
struct
{int a;char b;float c;
}x;
struct
{int a;char b;float c;
}a[20], *p;
上⾯的两个结构在声明的时候省略掉了结构体标签(tag)。
那么问题来了?
//在上⾯代码的基础上,下⾯的代码合法吗?
p = &x;
警告:
编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上 只能使⽤⼀次

1.3 结构的⾃引⽤

在结构中包含⼀个类型为该结构本⾝的成员是否可以呢?
⽐如,定义⼀个链表的节点:
struct Node
{int data;struct Node next;
};
上述代码正确吗?如果正确,那 sizeof(struct Node) 是多少?
仔细分析,其实是不⾏的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤ ⼩(字节)就会⽆穷的⼤, 是不合理的
正确的⾃引⽤⽅式:
struct Node
{int data;struct Node* next;
};

这个到后面数据结构会细讲。、

1.4typedef 对匿名结构体类型重命名

在结构体⾃引⽤使⽤的过程中,夹杂了 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. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对⻬数 = 编译器默认的⼀个对⻬数该成员变量⼤⼩的较⼩值
3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构 体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

扩展:

- VS 中默认的值为 8
- Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

 接下来用4个例子说明:

案例1:

struct S1
{char c1;int i;char c2;
};
printf("%d\n", sizeof(struct S1));//12

分析:

案例2:

struct S2
{char c1;char c2;int i;
};printf("%d\n", sizeof(struct S2));//8

分析:

 

案例3:

struct S3
{double d;char c;int i;
};
printf("%d\n", sizeof(struct S3));//32

分析:

 案例4:

练习4-结构体嵌套问题
struct S4
{char c1;struct S3 s3;double d;
};printf("%d\n", sizeof(struct S4));

分析:

2.2 为什么存在内存对⻬?  

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

2.3 修改默认对⻬数

#pragma 这个预处理指令,可以改变编译器的默认对⻬数,案例:

#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;
}

这样就可以修改默认的对齐数了。

结构体在对⻬⽅式不合适的时候,我们可以⾃⼰更改默认对⻬数。

3. 结构体传参

struct S
{int data[1000];int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参//第1种
void print1(struct S s)
{printf("%d\n", s.num);
}//第2种
//结构体地址传参
void print2(struct S* ps)
{printf("%d\n", ps->num);
}
int main()
{print1(s); //传结构体print2(&s); //传地址return 0;
}
上⾯的 print1 print2 函数哪个好些?
答案是:⾸选print2函数。
原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下 降。

结论: 

 也就是说:如果结构体过⼤,开辟空间大,不利于计算机的结束效率。

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

4. 结构体实现位段

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

1. 位段的成员 必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以
选择其他类型。
2. 位段的成员名后边 有⼀个冒号和⼀个数字(bit位个数)
⽐如:
struct A
{int _a:2;int _b:5;int _c:10;int _d:30;
};
A就是⼀个位段类型。
那位段A所占内存的⼤⼩是多少?
printf("%d\n", sizeof(struct A));

这是为什么呢?接下来我们来学习位段的内存分配。

4.2 位段的内存分配

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;

 分析:

总结:vs从左向右使用,遵循结构体对齐数原则,如果剩余的空间不够就浪费一定空间,开辟新空间。

4.3 位段的跨平台问题

1. int 位段被当成有符号数还是⽆符号数是不确定的。
2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会 出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃 剩余的位还是利⽤,这是不确定的。

 总结:跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

4.4位段使⽤的注意事项

位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位 置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。
所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊ 放在⼀个变量中,然后赋值给位段的成员。
如下:
struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
int main()
{struct A sa = {0};scanf("%d", &sa._b);//这是错误的//正确的⽰范int b = 0;scanf("%d", &b);sa._b = b;return 0;
}

结构体的学习就到这里了,都看到这里了,点一个赞吧,谢谢。 

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

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

相关文章

6.【自动驾驶与机器人中的SLAM技术】鲁邦核函数的含义和应用

目录 1. 给ICP和NDT配准添加柯西核函数1.1 代码实现 2. 将第1部分的robust loss引入IncNDTLO和LooselyLIO&#xff0c;给出实现和运行效果3. 从概率层面解释NDT残差和协方差矩阵的关系&#xff0c;说明为什么NDT协方差矩阵可以用于最小二乘4. 为LOAM like LO设计一个地面点云提…

从菜鸟到专业人士:来自真实 PRD 写作经验的 5 个改变游戏规则的产品管理课程

产品管理是一个复杂且具有挑战性的角色。PRD&#xff08;即产品需求文档&#xff09;就像构建产品的蓝图&#xff0c;可将其视为指导开发团队创造令人惊叹的产品的详细路线图。本文除了介绍产品经理如何写好PRD&#xff0c;也将进一步阐述产品经理应该学习哪些技能&#xff1f;…

玩转树莓派之系统安装篇

介绍 树莓派是树莓派基金会下的一个明星产品&#xff08;单板计算机&#xff09;&#xff0c;已经迭代到第五代了&#xff1b;它性能强大、开源、拓展性强、体积小&#xff0c;搞物联网开发的人基本都听说过这个玩意&#xff01;笔者手上刚好有一块4B的板子&#xff0c;让我们…

python封装执行cmd命令的方法

一、前置说明 在自动化时&#xff0c;经常需要使用命令行工具与系统进行交互&#xff0c;因此可以使用python封装一个执行cmd命令的方法。 二、代码实现 import subprocess import timefrom common.exception import RunCMDError from common.logger import loggerclass Cmd…

STM32储存器和总线构架

一、引言 本篇文章旨在介绍STM32小容量、中容量和大容量的储存器和系统构架&#xff0c;文中涉及到一些专有名词和概念较为抽象和陌生&#xff0c;建议读者能够查阅相关资料和知识加深了解。 二、正文 &#xff08;一&#xff09;、系统构架 在小容量、中容量和 大容量产品中…

在qemu平台使用gdb调试程序

1、使用gdb在qemu上调试程序 1.1、第一步&#xff1a;在qemu上运行程序并开启gdb server qemu-system-riscv64 -nographic -machine virt -m 128M -smp 1 -kernel …/bin/test.elf -s -S 1.2、第二步&#xff1a;使用gdb客户端连接gdb server -x&#xff1a;指定gdb的配置文件…

jmeter 压测需要的部分配置

修改jmeter 目录的bin目录下的jmeter.properties文件 解除KeepAlive设置 修改接口的高级中的实现和超时 解除httpclient4.retrycount前的注释符并将0修改为1 即修改为&#xff1a;httpclient4.retrycount1 解除httpclient4.idletimeout前的注释符并修改为合适间隔 即修改为…

创建型模式之工厂方法模式

一、概述 1、工厂方法模式&#xff1a;定义一个用于创建对象的接口&#xff0c;让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类 2、工厂方法模式&#xff1a;不再提供一个按钮工厂类来统一负责所有产品的创建&#xff0c;而是将具体的按钮创建过程交…

电子电工企业品牌网站建设的作用是什么

电子电工企业在市场中有较高的需求度&#xff0c;比如电子元件、电子产品等&#xff0c;这些都属于高信任度产品&#xff0c;对需求方来说&#xff0c;需要查看商家全部信息、包括资质、产品/服务内容、案例等&#xff0c;因此对电子电工企业来讲&#xff0c;需要贯通品牌路径&…

解决RuntimeError: CUDA error: invalid device ordinal

步骤 首先查看自己设备的cuda版本 #如下linux指令都可以&#xff0c;主要还是以nvidia-smi为主 nvidia-smi nvcc -V用的python版本是3.8 torch版本用的1.12.1cu113 torch网址&#xff1a;https://pytorch.org/get-started/previous-versions/ 安装完后发现出现如下问题&#…

前端路由钩子的神奇之处:你真的了解它们吗?(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

CSS 基础

文章目录 CSS 常见的属性CSS 常见样式行内样式内嵌样式导入样式 CSS 选择器标签选择器id选择器类选择器全局选择器属性选择器组合选择器 CSS 常见应用表格列表导航栏下拉菜单提示工具图片廊 CSS (Cascading Style Sheets&#xff0c;层叠样式表&#xff09;&#xff0c;是一种用…

数据库连接池Druid

在 Spring Boot 项目中&#xff0c;数据库连接池已经成为标配&#xff0c;然而&#xff0c;我曾经遇到过不少连接池异常导致业务错误的事故。很多经验丰富的工程师也可能不小心在这方面出现问题。 在这篇文章中&#xff0c;我们将探讨数据库连接池&#xff0c;深入解析其实现机…

【卡塔尔世界杯数据可视化与新闻展示】

卡塔尔世界杯数据可视化与新闻展示 前言数据获取与处理可视化页面搭建功能实现新闻信息显示详情查看登录注册评论信息管理 创新点结语 前言 随着卡塔尔世界杯的临近&#xff0c;对于足球爱好者来说&#xff0c;对比赛的数据分析和新闻报道将成为关注的焦点。本文将介绍如何使用…

openmediavault debian linux安装配置企业私有网盘(三 )——raid5与btrfs文件系统无损原数据扩容

一、适用环境 1、企业自有物理专业服务器&#xff0c;一些敏感数据不外流时&#xff0c;使用openmediavault自建NAS系统&#xff1b; 2、在虚拟化环境中自建NAS系统&#xff0c;用于内网办公&#xff0c;或出差外网办公时&#xff0c;企业内的文件共享&#xff1b; 3、虚拟化环…

jmeter配置使用(mac)

前言 这篇文件就是一个笔记&#xff0c;非mac用户不用看了&#xff0c;我这是换了mac&#xff0c;要用jmeter的倒腾。 一、下载 二、使用步骤 1.解压 tgz格式的直接用tar命令就行 tar -zxvf 包名2.启动 一种是进入解压包的bin目录启动 这种方式启动的就是命令框不能关闭&am…

正则表达式详解

什么是正则表达式 正则表达式&#xff0c;又称规则表达式&#xff0c;通常被用来检索、替换那些符合某个模式(规则)的文本。 正则表达式是对字符串操作的一种逻辑公式&#xff0c;就是用事先定义好的一些特定字符、及这些特定字符的组合&#xff0c;组成一个"规则字符串…

小区生活污水处理需要哪些设备和工艺

在小区生活中&#xff0c;污水处理是一个非常重要的环节&#xff0c;它关乎到环境的保护和居民的生活质量。因此&#xff0c;了解小区生活污水处理所需要的设备和工艺是至关重要的。 首先&#xff0c;在小区生活污水处理中&#xff0c;需要用到的设备包括污水收集系统、初级沉淀…

多微信聚合聊天:一款简便的管理工具帮你摆脱微信繁琐之困!

在这个信息飞速传递的时代&#xff0c;微信已经成为了人们不可或缺的通信工具之一。然而&#xff0c;由于个人、工作等各种原因&#xff0c;我们可能会拥有多个微信号&#xff0c;而每天切换不同的账号进行聊天&#xff0c;无疑是一项繁琐的任务。 近日&#xff0c;我一位好友向…

互联网加竞赛 python+opencv+机器学习车牌识别

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于机器学习的车牌识别系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;3分 该项目较为新颖&#xff0c;适…