C语言:自定义类型:结构体

news2024/9/20 4:50:24

目录

1. 前言

2. 结构体初识

3. 结构体创建变量

3.1 方法一

3.2 方法二

4. 结构体初始化

5. 结构体自引用

6. 结构体的大小

6.1 结构体对齐规则

6.2 常规结构体

6.3 结构体成员含数组

6.4 结构体嵌套结构体

6.5 为什么存在结构体对齐?

6.6 修改默认对齐数

7. 结构体传参

8. 结构体实现位段

8.1 位段基本讲解

8.2 位段的作用


1. 前言

C语言中有很多种类型,例如:
int ,float ,char ,double, long  ,long long ,long double等等,这些叫C语言的内置类型

但是有的时候描述一个人或物只用某一个类型是明显不够的

这时候C语言提供了自定义类型:结构体

2. 结构体初识

我们可以在结构体里面自己使用C语言的内置类型构造我们想要的自定义类型

比如我们需要描述一个学生

struct student
{
    char name[20];
    int num;
    int age;
};

学生有姓名,学号,年龄等等属性,并不是一个int或者char就能表示的,所以我们可以自定义一个类型,里面有姓名,学号,年龄,这些属于结构体的成员,这个struct student 就是我们自己定义的类型

结构体的最后有个分号一定不能漏掉

有了类型,我们就可以跟其它的内置类型一样,创建变量

3. 结构体创建变量

int a = 10;
char c = 'l';

上面我们就用了int 类型和 char类型创建了两个变量a 和 c,然后给它们赋值

结构体也是如此

3.1 方法一

struct student
{
    char name[20];
    int num;
    int age;
};

int main()
{
    struct student stu;
    return 0;
}

我们用自己创建的类型后面加上变量名字,这样就创建好了一个变量stu

3.2 方法二

struct student
{
    char name[20];
    int num;
    int age;
}stu;

在创建好的结构体后面分号前面直接加变量名字即可

4. 结构体初始化

#include<stdio.h>
#include<string.h>

struct student
{
    char name[20];
    int num;
    int age;
};

int main()
{
    struct student stu;
    strcpy(stu.name, "xiaoming");
    stu.age = 18;
    stu.num = 1;
    return 0;
}

我们要在结构体变量的后面加个. 才能找到结构体里的成员

然后我们如果给name这个字符数组初始化的话需要使用string.h库里的strcpy来给它赋值

然后下面的age 和 num一样都是用 变量+.操作符找到成员

int 类型就正常赋值即可

然后可以正常打印输出了

printf("名字:%s\n", stu.name);
printf("学号:%d\n", stu.num);
printf("年龄:%d\n", stu.age);

5. 结构体自引用

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

上面的结构体成员里嵌套了一个自身结构体成员next

这个就是结构体嵌套,自引用

但是这样的写法是错误的❌

因为在我们计算结构体大小的时候,还要计算里面的next的大小,但是里面的next又包含了下一个data和下一个next,这样就无限循环下去了导致结构体大小无穷大,是不合理的

正确写法✔:

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

 这也是链表的写法

错误写法❌:

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

 因为这个结构体只有创建完成了才会重命名为Node,所以在创建完成前成员里使用Node是不对的

 正确写法✔:

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

6. 结构体的大小

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

猜猜上面代码的结果是什么?

如果你觉得char占1个字节,int占4字节,char占1个字节总共6个字节那么就掉坑了

结构体其实是有它自己的一个对齐规则的

6.1 结构体对齐规则

规则:

1.结构体第一个成员对齐到结构体变量起始位置偏移量为0的地址处

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处        

对齐数 = 编译器默认对齐数 与 该成员变量大小的较小值

3.结构体大小为最大对齐数的整数倍(选所有对齐数中最大的那一个)

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

先来一个个解释一下

6.2 常规结构体

如果这是我们的内存空间,那么我们在存放的时候会先从结构体的第一个成员所占的空间开始占用第一个0

按照上面我们写的结构体,那么第一个char就会占用一个0

然后根据第二个对齐规则,其他成员要对齐到对齐数的整数倍

那么我们要先算对齐数,下一个是int类型,所以算int类型的对齐数

对齐数 = 编译器默认对齐数 与 该成员变量大小的较小值

如果用的是VS,编译器默认对齐数就为8,这是固定的

该成员(int类型)大小为4个字节

所以选择小的作为对齐数所以对齐数就是4

所以我们要占用从4的整数倍开始的4个字节

下一个类型是char,char的字节是1,取最小值作为对齐数当然对齐数就是1了

所以下一个8就是1的倍数,所以直接占用即可

再根据第三个规则

我们现在全部的对齐数最大的为int类型的4,所以整个结构体的大小要为4的整数倍

但是我们看到上图中从0-8有9个字节占用了

所以我们还要再次浪费三个字节的空间满足这个规则

所以最终如图:

所以最后从0-11总共是用了12个字节,那么最终输出的结果当然就是12了

6.3 结构体成员含数组

如果是结构体成员里含数组的情况,其实跟常规的结构体一样

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

 这个char数组其实没什么特别的,就把它看成5个char类型的东西即可

然后这5个char类型都是对齐数为1,然后最后再根据最大对齐数int的4对齐结构体总大小即可

如图:

所以是16个字节

下面来看看嵌套:

6.4 结构体嵌套结构体

struct S
{
 char c1;
 struct S1 s1;
 double d;
};

老样子char先占用0偏移量

然后我们上面已经计算过了结构体S1的最大对齐数为4,所以结构体S1对齐数为4

根据第4条对齐规则,可以画出如图:

然后double是8个字节,所以从8的倍数就是16开始到23的这个位置

在最后计算结构体总大小对齐的位置时,嵌套的结构体并不会用总大小参与,而是用它的成员来与其他成员比较,里面明显是double的大小最大,所以按8个字节的整数倍来定义结构体的大小

图中0-23正好是24(8的整数倍),所以最终答案是24

6.5 为什么存在结构体对齐?

我们看到上面的每一个结构体几乎都浪费了很多空间,但这么做其实是有好处的

这是一个以空间换时间的做法

假设一个处理器总是一次性从内存中取8个字节,则拿到的地址对应的都是8的倍数

如果我们能够保证能将double类型的数据都对齐成8的倍数

那么处理器一次扫描就能够将整个double全部取出

而不是扫描两次

这样效率就快了很多

如果我们想要两者兼得,如何做到呢?

目前没什么特别好的方法,但是我们可以尽可能让空间小的成员尽可能的聚在一起

例如:

struct S1
{
    char c1;
    int i;
    char c2;
};

struct S2
{
    char c1;
    char c2;
    int i;
};

它们两个的成员一模一样,但是

第一个结构体需要12个字节的空间,而第二个只需要8个

6.6 修改默认对齐数

 我们其实是可以自己更改默认对齐数的

#pragma pack(1)//默认对齐数改为1
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消修改默认对齐数

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

结构体成员的对齐数都为1那结构体的总大小就是全部成员的大小相加

所以答案为: 

7. 结构体传参

结构体也是和其他内置类型一样作为参数传递给函数的

我们之前有写到这个代码

#include<stdio.h>
#include<string.h>

struct student
{
    char name[20];
    int num;
    int age;
};

int main()
{
    struct student stu;
    strcpy(stu.name, "xiaoming");
    stu.age = 18;
    stu.num = 1;
    return 0;
}

现在可以用一个Print函数来打印试试看

#include<stdio.h>
#include<string.h>

struct student
{
    char name[20];
    int num;
    int age;
};

void Print(struct student s)
{
    printf("%s\n", s.name);
    printf("%d\n", s.age);
    printf("%d\n", s.num);
}

int main()
{
    struct student stu;
    strcpy(stu.name, "xiaoming");
    stu.age = 18;
    stu.num = 1;
    Print(stu);
    return 0;
}

我们创建了一个 struct student类型的形参s

然后对这个形参进行打印

在传参的时候,我们只需要将我们之前初始化过的 stu传进去即可 

这样一样是可以的

8. 结构体实现位段

struct A
{
 int a:2;
 int b:5;
 int c:10;
 int d:30;
};

我们在成员之后加了个:+数字就是位段

什么意思呢?

8.1 位段基本讲解

这些数字表示当前成员所占用的bit的大小

总所周知,一个int类型为4个字节也就是32个bit

所以我们:后面的数字一定不能超过32,因为一个int类型最多32个bit位

8.2 位段的作用

它可以帮我们节省空间,如果我们要开装下成员a和b和c,那么我们只需要开一个int类型即可

32个空间足以装下 2+5+10 = 17的空间

注意事项:
位段尽量不要跨平台使用,因为C语言并未完全的定义位段如何使用,所以不同的编译器都会有它自己的处理方式,所以不要跨平台使用!!!


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

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

相关文章

软考高级:软件架构评估-质量属性:可用性概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

Golang Gorm 自动分批查询

场景&#xff1a; 目标查询全量数据&#xff0c;但需要每次Limit分批查询&#xff0c;保护数据库 文档&#xff1a; https://gorm.io/zh_CN/docs/advanced_query.html // Param: // dest 目标地址 // batchSize 大小 // fc 处理函数func (db *DB) FindInBatc…

leetcode 18.四数之和 java

题目 思路 整体在三数之和的基础上进行修改。&#xff08;所有需要修改的地方&#xff0c;我在代码里加了//改 的注释&#xff09; 大的一个思路就是&#xff0c;在三数之和的外面再套一层循环。相当于固定前两个数。然后这道题目标值变成一个参数了&#xff0c;不是三数之和…

上位机图像处理和嵌入式模块部署(qmacvisual轮廓查找)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们说过&#xff0c;图像的处理流程一般都是这样的&#xff0c;即灰度化-》降噪-》边缘检测-》二值化-》开闭运算-》轮廓检测。虽然前面的几个…

Springboot 整合 Knife4j (API文档生成工具)

目录 一、Knife4j 介绍 二、Springboot 整合 Knife4j 1、pom.xml中引入依赖包 2、在application.yml 中添加 Knife4j 相关配置 3、打开 Knife4j UI界面 三、关于Knife4j框架中常用的注解 1、Api 2、ApiOperation ​3、ApiOperationSupport(order X) ​4、ApiImplici…

模态框被div class=modal-backdrop fade in覆盖的问题

模态框被<div class"modal-backdrop fade in">覆盖的问题 起因&#xff1a;在导入模态框时页面被一层灰色的标签覆盖住 F12查看后发现是一个<div class"modal-backdrop fade in"> 一开始以为是z-index的问题&#xff0c;但经过挨个修改后感觉…

SpringBoot项目如何打包成war包,并部署在tomcat上运行

项目场景&#xff1a; 正常情况下&#xff0c;我们开发 SpringBoot 项目&#xff0c;由于内置了Tomcat&#xff0c;所以项目可以直接启动&#xff0c;部署到服务器的时候&#xff0c;直接打成 jar 包&#xff0c;就可以运行了。 有时我们会需要打包成 war 包&#xff0c;放入外…

【漏洞复现】福建科立迅通信指挥调度平台down_file.php sql注入漏洞

漏洞描述 福建科立迅通信调度平台 20240318 以及之前版本存在一个严重漏洞,影响了文件 api/client/down_file.php 的一个未知功能。攻击者可以通过操纵参数 uuid 发起 SQL 注入攻击。攻击者可以远程发起攻击。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守…

ROS机器人入门第一课:ROS快速体验——python实现HelloWorld

文章目录 ROS机器人入门第一课&#xff1a;ROS快速体验——python实现HelloWorld一、HelloWorld实现简介&#xff08;一&#xff09;创建工作空间并初始化&#xff08;二&#xff09;进入 src 创建 ros 包并添加依赖 二、HelloWorld(Python版)&#xff08;二&#xff09;进入 r…

Axure 中继器的Repeater属性的使用

dataCount 中继器当中存在多少条数据&#xff0c;总数。 visibleltemCount 中继器列表中可见项数量&#xff0c;也就是当前页面显示的数量。 pageCount 获取中继器分页的总数量&#xff0c;即能够获取分页后共有多少页。 pageIndex 获取中继器当前显示的页码

易大师B版运势测算系统源码-八字周易运势塔罗-含视频搭建教程

2024最新易大师B版运势测算系统源码-八字周易运势塔罗等测算源码 基于上个版本再次做了数据优化和部分bug修复&#xff0c;青狐独家维护版本。 测算周易系统一直都是很好变现和运营的&#xff0c;玩法操作也比较简单&#xff0c;也很容易被百度收录推广。 大致功能&#xff1a…

关系型数据库mysql(3)索引

目录 一.索引的概念 二.索引的作用 三.创建索引的原则依据 四.索引的分类 五.索引的创建 5.1 普通索引 5.1.1 直接创建索引 5.1.2 修改表方式创建 5.1.3 创建表的时候指定索引 5.2 唯一索引 5.2.1 直接创建唯一索引 5.2.2 修改表方式创建 5.2.3 创建表的时候指…

NX二次开发控制Button的敏感性(是否可用)

一、概述 最近看到一个控制Button灵敏度的功能觉得有点意思&#xff0c;今后的二次开发中避免不了使用&#xff0c;今天做以下笔记&#xff0c;信息来源于【小C-NX】的博客。俗话说的好&#xff0c;三人行必有我师焉。再次致敬伟大的NX二次开发爱好者&#xff0c;思维的碰撞可能…

【性能测试】移动测试md知识总结第2篇:主流移动端自动化测试工具,学习目标【附代码文档】

移动测试完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;移动端测试课程介绍&#xff0c;移动端测试知识概览学习主要内容,学习目标,学习目标,1. window安装andorid模拟器。主流移动端自动化测试工具&#xff0c;Appium环境搭建学习目标,学习目标,学习目标…

vue3父子通信、跨层通信

子传父 通过 ref标识 获取真实的 dom对象或者组件实例对象 父组件获取子组件内部属性和方法 顶层组件向任意的底层组件传递数据和方法&#xff0c;实现跨层组件通信 非响应式数据父修改不了子的内容 子组件调用父组件方法

yolov9报错:AttributeError: ‘list‘ object has no attribute ‘view‘的两种解决方法

1. 报错问题 In loss_tal.py: pred_distri, pred_scores torch.cat([xi.view(feats[0].shape[0], self.no, -1) for xi in feats], 2).split( (self.reg_max * 4, self.nc), 1) The error is as follows&#xff1a; AttributeError: list object has no attribute vie…

显卡基础知识及元器件原理分析

显卡应该算是是目前最为火热的研发方向了&#xff0c;其中的明星公司当属英伟达。 当地时间8月23日&#xff0c;英伟达发布截至7月30日的2024财年第二财季财报&#xff0c;营收和利润成倍增长&#xff0c;均超市场预期。 财报显示&#xff0c;第二财季英伟达营收为135.07 亿美…

【题目】【网络系统管理】2019年全国职业技能大赛高职组计算机网络应用赛项H卷

极安云科专注职业教育技能竞赛培训4年&#xff0c;包含信息安全管理与评估、网络系统管理、网络搭建等多个赛项及各大CTF模块培训学习服务。本团队基于赛项知识点&#xff0c;提供完整全面的系统性理论教学与技能培训&#xff0c;成立至今持续优化教学资源与讲师结构&#xff0…

【毕设级项目】基于ESP8266的家庭灯光与火情智能监测系统——文末源码及PPT

目录 系统介绍 硬件配置 硬件连接图 系统分析与总体设计 系统硬件设计 ESP8266 WIFI开发板 人体红外传感器模块 光敏电阻传感器模块 火焰传感器模块 可燃气体传感器模块 温湿度传感器模块 OLED显示屏模块 系统软件设计 温湿度检测模块 报警模块 OLED显示模块 …

Postgresql数据库入门简介

Postgresql入门 1.Postgresql数据库简介 PostgresQL是一个功能强大的开源数据库系统。经过长达15年以上的积极开发和不断改进&#xff0c;PostgreSQL已在可靠性、稳定性、数据一致性等获得了业内极高的声誉。目前PostgreSql可以运行在所有主流操作系统上&#xff0c;包括Linux…