C语言结构体介绍(超详细)

文章目录

  • 每日一言
  • 结构体是什么?
  • 为什么要使用结构体?
  • 如何使用结构体?
    • 结构体的定义
    • 结构体的声明
    • 如何访问结构体中的成员
  • 结构体内存对齐
    • 什么是结构体内存对齐
      • 对齐规则
        • 修改默认对齐数
    • 为什么存在结构对齐
  • 总结
  • 结语

每日一言

Develop your imagination – you can use it to create in your mind what you hope to create in your life.
发展你的想像力 – 你可以用它在心里创造你想在现实生活创造的东西。


结构体是什么?

结构体是一种自定义的数据类型,可以包含多个不同类型的数据成员,并可以根据这些成员进行组合和访问。结构体可以用于表示一个具有多个属性的实体或对象,例如一个人的姓名、年龄和性别,一个车辆的品牌、型号和颜色,等等。

为什么要使用结构体?

C语言已经为我们提供了许多的内置类型,如:int、long、float、double、char、short等,但这些类型只能描述相对单一的事物。
假如我要描述一个人,人是一个相对复杂的事物,他有姓名、年龄、性别、身份证号、体重、电话号码等属性。这时如果只用上面给出的类型是很难描述一个人的所有信息的。

如何使用结构体?

结构体的定义

struct 结构体名称 {数据类型 变量名1;数据类型 变量名2;// ...
};

例如用结构体描述一个人:

struct Person {char name[20];//名字int age;//年龄char gender;//性别char id_card[18];//身份证号float weight;//体重char phone[11];//电话
};

结构体的声明

声明结构体变量的格式如下:

struct 结构体名 变量名;

例如,声明一个名为awa,年龄为18岁,性别为男,身份证号为666666,体重为60.2,电话为1234567的人:

方式一
声明结构体变量时直接定义结构体

代码如下:

struct Person {char name[20];//名字int age;//年龄char gender;//性别char id_card[18];//身份证号float weight;//体重char phone[11];//电话
}awa = {"awa",18,'M',"666666",60.2,"1234567"};

方式二
先定义结构体类型,再声明结构体变量

代码如下:

struct Person {char name[20];//名字int age;//年龄char gender;//性别char id_card[18];//身份证号float weight;//体重char phone[11];//电话
};struct Person awa = {"awa",18,'M',"666666",60.2,"1234567"};

两种方式的本质区别在于第一种方式在定义结构体变量的同时也定义了结构体类型,而第二种方式分别定义了结构体类型和结构体变量。而在实际应用中,通常会使用第一种方式,代码会更加简洁明了。 C语言也支持在结构体中嵌套定义其他结构体。

如何访问结构体中的成员

我们依然以上面的awa为例,如何在屏幕上打印出awa的电话呢?
我们有两种方法:

法一:
通过点操作符来访问结构体成员。

代码如下:

printf("awa的电话是:%s\n", awa.phone);

法二:
通过指针操作符来访问结构体成员。

代码如下:

struct Person *p;//定义一个指针p,它的类型是struct Person。
p = &awa;//将p指向结构体变量awa的地址。
printf("awa的电话是:%s\n", p->phone);

结构体内存对齐

现在我们对结构体有了大致的了解,接下来我们来谈一谈结构体是如何在内存中存储的。只有了解了它的本质,我们用起来就更加得心应手。

什么是结构体内存对齐

内存对齐是指将数据存储在内存中时,按照某种规则对其进行填充,使其在访问时的速度更快。这主要是因为CPU的读取速度与地址对齐有关。

对齐规则

  1. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
  2. 对齐数 = 编译器默认的一个数与该成员变量大小的较小值
  3. 结构体总是大小为最大对齐数的整数倍
  4. 如果是嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍

例:
求AA_t 所站的字节大小(假如编译器默认对齐数为8字节)

typedef struct{int a;char b;short c;short d;
}AA_t;

计算过程:
在这里插入图片描述

注意:
VS中默认的对齐数为8
Linux中gcc没有默认对齐数,对齐数就是成员自身的大小

修改默认对齐数

#pragma 这个预处理指令可以改变编译器的默认对齐数

例:
#pragma pack(1)
设置默认对齐数为1

#pragma pack()
取消设置的默认对齐数

一般不建议修改默认对齐数,它会导致许多问题。

1.可能会破坏结构体的内存对齐,导致结构体成员的访问速度变慢,因为不是按照CPU的最佳对齐方式进行的。

2.可能会增加内存的浪费。如果对齐数太小,内存对齐不足,会导致内存浪费,因为需要加入一些填充字节来填充对齐差距。

3.可能会影响平台的可移植性。不同的平台对内存对齐的要求可能不同,因此在不同的平台上运行代码时可能会出现错误或不一致的结果。

4.可能会使代码变得难以维护,并且会增加调试难度。更改默认对齐数通常是非标准的做法,开发人员可能需要花费更多时间来解决由此引起的问题。

为什么存在结构对齐

通过对结构体进行对齐,可以保证每个结构体成员访问时都能够在一个内存块中连续访问,提高了代码的执行效率。同时,结构体对齐也能够减少内存碎片的产生,进一步提高内存的利用效率。

注意:
内存对齐可以提高数据访问的效率,但同时也会增加内存空间的浪费。因此,在设计结构体时,需要权衡对齐对空间利用率和访问效率的影响,以达到最优的设计。
通常我们尽量让占用空间小的成员集中在一起

总结

  1. 结构体的定义:需要使用关键字struct,语法格式如下:
struct 结构体名称 {数据类型 变量名1;数据类型 变量名2;// ...
};
  1. 成员变量的定义:可以是基本数据类型、指针类型、数组类型等。定义时需要指明变量名和数据类型。

  2. 结构体的大小:结构体的大小是所有成员变量大小的累加,需要考虑对齐方式和填充字节。

  3. 结构体的初始化:可以采用两种方式进行初始化,一种是在定义时赋值,另一种是通过赋值运算符对结构体对象进行初始化。

  4. 结构体的成员访问:可以通过".“或”->“运算符进行成员访问,”.“用于访问结构体对象的成员,”->"用于访问指向结构体的指针的成员。

  5. 结构体的传参:可以将结构体作为函数参数传递,可以通过传值或传指针的方式进行传参。

  6. 结构体的对齐方式:需要指明对齐方式,可以通过#pragma pack指令或__attribute__((packed))关键字来进行设置。


结语

请给自己些耐心,一口吃不成胖子。
山外青山楼外楼,莫把百尺当尽头。
保持空杯心态加油努力吧!


都看到这里啦!真棒(*^▽^*)

可以给作者一个免费的赞赞吗,这将会鼓励我继续创作,谢谢大家

编程小白写作,如有纰漏或错误,欢迎指正


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

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

相关文章

Android : 篮球记分器app _简单应用

示例图: 1.导包 在build.gradle 中 加入 // 使用androidx版本库implementation androidx.lifecycle:lifecycle-extensions:2.1.0-alpha03 2. 开启dataBinding android{...// 步骤1.开启data bindingdataBinding {enabled true}...} 3.写个类继承 ViewModel pac…

dockerdesktop 制作asp.net core webapi镜像-连接sqlserver数据库容器

1.使用visual studio 创建 asp.net core webapi项目 选择启用docker 会生成Dockerfile文件 2.使用efcore连接数据库&#xff0c;安装efcore的包 <ItemGroup><PackageReference Include"Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version&qu…

《Pandas1.x实例精解 》书籍分享

Pandas介绍 Pandas&#xff1a;Python数据分析的瑞士军刀 在数据科学、机器学习和人工智能日益繁荣的今天&#xff0c;有效、准确地处理和分析数据已经成为了成功的关键。Python&#xff0c;作为一种强大且易于学习的编程语言&#xff0c;已经在这一领域占据了重要的地位。而…

P4715 【深基16.例1】淘汰赛-仅思路

首先从题干要求入手&#xff0c;我们可以了解到题目要求是二进一&#xff0c;不难想到这是二叉树的题 再来&#xff0c;从题干可以知道&#xff0c;我们所采用的结构体除了需要有树的两个左右节点指针外&#xff0c;还需要两个变量用来储存“能力值”和“编号” 在这道题中&am…

MyBatis 常见面试题

目录 1.MyBatis——概述1.1.什么是 ORM 框架&#xff1f;1.2.✨谈谈对 MyBatis 的理解。1.3.使用 MyBatis 相对于直接使用 SQL 有哪些优点&#xff1f;1.4.MyBatis 有什么优缺点&#xff1f;1.5.✨MyBatis 的分层结构是什么样的&#xff1f;1.6.✨MyBatis 的执行流程是什么样的…

学习设计模式的网站

Refactoring and Design Patternshttps://refactoring.guru/

【Pytorch使用自制数据集,Dataloader】

数据集结构 话不多说&#xff0c;直接上核心代码 myDataset.py from collections import Counter from torch.utils.data import Dataset import os from PIL import Imageclass MyDataset(Dataset):"""读取自制的数据集args:- image_dir: 图片的地址- labe…

uniapp横向滚动示例

目录 插件市场案例最后 插件市场 地址 案例 地址 最后 感觉文章好的话记得点个心心和关注和收藏&#xff0c;有错的地方麻烦指正一下&#xff0c;如果需要转载,请标明出处&#xff0c;多谢&#xff01;&#xff01;&#xff01;

如何使用Python核对文件夹内的文件

说明&#xff1a;日常工作中&#xff0c;我们经常会遇到这样的场景&#xff1a;核对A、B文件夹中文件的差异&#xff0c;找出A、B文件夹中不同部分的文件&#xff1b; 本文介绍如何使用Python来实现&#xff1b; 第一步&#xff1a;获取文件清单 首先&#xff0c;我们要获取…

Pyside2 (Qt For Python)进度条功能实现

Pyside2 (Qt For Python)进度条功能实现 进度条&#xff08;QProgressBar&#xff09; 今天来介绍PySide2进度条&#xff08;QProgressBar&#xff09;的使用&#xff0c;如下所示&#xff1a; 说明 进度条也是一个常用的控件&#xff0c;当程序需要做一件比较耗费时间的任…

java开发神器之ecplise的基本使用

java开发神器之ecplise的基本使用 一、ecplise的安装二、利用ecplise创建工作空间 一、ecplise的安装 免安装eclipse程序包 二、利用ecplise创建工作空间 1、准备好eclipse的程序包&#xff0c;右键执行程序。 2、若打开eclipse显示如下第一张图的界面提示&#xff0c;是因…

【优选算法】1089.复写零

链接&#x1f517;&#xff1a;1089. 复写零 - 力扣&#xff08;LeetCode&#xff09; 一&#xff0c;题目解析 要点均用红框圈起来了&#xff0c;特别注意 不要超过数组长度&#xff01;&#xff01;&#xff01; 二&#xff0c;算法原理 通过双指针算法来实现 主要步骤如…