自定义类型 - 结构体

news2024/11/24 9:12:14

2024 - 10 - 13 - 笔记 - 26
作者(Author): 郑龙浩 / 仟濹(CSDN账号名)

自定义类型 - 结构体

平时用的数组是一组相同类型的数据,如果想表示一组不同类型的数据,那么就可以结构体了。

① 结构体的声明(重要)

自己起的名字,为了方便记忆和复盘,并不是规范的名字

  1. 直接使用法(只能使用一次)

    【匿名结构体类型】

    不写结【结构体标签(结构体名)】,只写【结构类型的变量】,所以只能使用一次

    struct
    {
        char name[20];
        char sex[10];
    }BB;//BB 为机构体变量名
    
  2. 直接声明法 (结构体可以使用多次,或者叫类型可以使用多次)

    写机构体名,所以后续想继续使用此机构体类型,仍可以定义此类型的结构类型的变量

    struct AA //AA 为机构体名
    {
        char name[20];
        char sex[10];
    };
    struct AA BB, CC;
    //BB 和 CC就是机构体变量名
    
  3. 直接定义法

    声明结构体的同时也声明了【该结构类型的 变量 】。

    struct AA //AA 为机构体名 / 结构体标签
    {
        char name[20];
        char sex[10];
    }ABC;
    struct AA BB, CC;
    //BB 和 CC就是机构体变量名,ABC 也是【该结构类型的 变量 】
    
  4. 类型定义法

    typedef struct AA //AA 为结构体名
    {
        char name[20];
        char sex[10];
    }ABC;//重新命名,可以想成 ABC 代替了 struct AA,方便后期使用。
    
    
    //此时这里的ABC就不是结构体变量了,而是将结构体类型重新写了一个名字,方便使用,否则每次定义机构体变量都要写struct AA,这样再次定义结构体变量就可以直接使用ABC作为类型了。
    struct AA BB, CC;
    ABC BB, CC;//效果与struct AA BB, CC;相同
    
  5. 结构体的自引用

    在定义结构体类型的时候嵌套一个该结构的结构变量是可行的吗???

    不可行!!!

    第 ① 种:

    错误写法:

    //这中写法是错误的,试着读一下,这是一个嵌套又嵌套又嵌套,无限套娃的一个结构体,编译器是不会执行的
    struct Node
    {
         int data;
         struct Node next;
    };
    

    正确写法:

    //既然无法在某结构类型中,嵌套该结构类型的变量,那么我可以在定义该类型的时候,创建一个该结构类型的指针
    struct Node
    {
         int data;
         struct Node* next;
    };
    

    第 ② 种

    错误代码:

    //在重命名的时候也是不可行的
    typedef struct
    {
     int data;
     Node* next;
    }Node;
    
    

    正确代码

    typedef struct Node
    {
     int data;
     struct Node* next;
    }Node;
    
②结构体的初始化,结构体成员的赋值

下面举例的这几种方法都是可以的(随便写的几个例子):

    struct AA 
    {
        char name[20];
        int age;
    }ABC = { "aaa", 10 };
    
    struct AA 
    {
        char name[20];
        int age;
        struct BB
        {
            int  days;
            char ch; 
        }
    }ABC = { "aaa", 10, { 100, 'a' } };
    
    struct AA 
    {
        char name[20];
        int age;
    };
    struct AA ABC = { "aaa", 10 };
    
    typedef struct 
    {
        char name[20];
        int age;
    }AA;
    AA ABC = { "aaa", 10 };
    
③ 结构体的内存对齐 - 结构体大小的计算(非常重要!!!)
1) 例题

想一下,结构体的大小是怎么计算的呢???

#include <stdio.h>
//练习1
struct S1
{
    char c1;
    int i;
    char c2;
};

//练习2
struct S2
{
    char c1;
    char c2;
    int i;
};

//练习3
struct S3
{
    double d;
    char c;
    int i;
};

//练习4-结构体嵌套问题
struct S4
{
    char c1;
    struct S3 s3;
    double d;
};

int main()
{
    printf("%d\n", sizeof(struct S1));//12
    printf("%d\n", sizeof(struct S2));//8
    printf("%d\n", sizeof(struct S3));//16
    printf("%d\n", sizeof(struct S4));//32

    return 0;
}

打印结果:

12
8
16
32

关于结构体如何计算大小,其实还是稍微复杂一些的。

2) 结构体【内存对齐】规则
  1. 第一个成员在与结构体变量偏移量为0的地址处。

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

    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值,即**【默认对齐数】【该成员对其数】进行比较,哪个小,就是真正的对齐数。(VS编译器中默认的值为8,其他好多编译器没有所谓的【默认对齐数】,也就不用将【成员对齐数】【默认对齐数】**进行比较了)

    比如 int a 【成员对其数】是4,VS编译器【默认对齐数】是8,取两者较小值,为4,所以对齐数就是 4 .

  3. 每个结构体成员会按照其数据类型的大小和对齐要求来分配内存。

  4. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。即使结构体的最后一个成员之后没有足够的空间来满足最大对齐要求,编译器也会添加填充字节(padding)来达到这一要求。

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

3) 为什么存在【内存对齐】
  1. 硬件访问效率:
    • 现代计算机的内存访问通常是以块或字节对齐的方式进行的。
    • 如果数据没有正确对齐,处理器可能需要额外的时钟周期来访问数据,这会导致性能下降。
  2. 简化硬件设计:
    • 内存对齐使得硬件设计更加简单和高效。
    • 处理器不需要处理复杂的地址计算和数据拆分/合并操作。
  3. 提高缓存命中率:
    • 缓存通常是以块为单位进行操作的,这些块的大小通常是2的幂次方(如64字节)。
    • 如果数据按缓存块大小对齐,则更容易完全装入缓存块中,从而提高缓存命中率。
  4. 数据一致性和安全性:
    • 某些数据类型(如浮点数)在内存中需要特定的对齐方式才能保证数据的正确性和一致性。
    • 不正确的对齐可能导致数据被错误地解释或损坏。
  5. 便于多处理器系统同步:
    • 在多处理器系统中,内存对齐有助于简化数据同步和一致性问题。
    • 对齐的数据更容易进行原子操作,从而避免数据竞争和不一致性。

注:【内存对齐】是一种 拿 【空间】 来换取 【时间】的一种做法

4) 解释结构体如何计算大小 、 如何进行【内存对齐】的
#include <stdio.h>
struct S1
{
 char c1;
 int i;
 char c2;
};
struct S2
{
 char c1;
 char c2;
 int i;
};

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

打印结果:

12
8

成员类型都是一样的,只是换了成员的顺序,为什么结构体大小就不同了呢?

下面我作图进行分析:

郑龙浩_结构体内存对齐(最终版)(水印)(2)

5) 结构体传参 - 最好用第二种方法,传结构体地址
struct AA
{
 int data[1000];
 int num;
};
struct AA BB = {{1,2,3,4}, 1000};

//结构体传参
void print1(struct AA BB)
{
	int i = 0;
    for( i = 0; i < 3 ; i ++ )
    {
        printf("%d", BB.date[i] );
    }
     printf("%d\n", BB.num);
}
//结构体地址传参
void print2(struct AA* p)
{
int i = 0;
    for( i = 0; i < 3 ; i ++ )
    {
        printf("%d", p -> date[i] );
    }
     printf("%d\n", p -> num);
}
int main()
{
 print1(BB);  //传结构体
 print2(&AA); //传地址
 return 0;
}
  • 第 1 种是传【值】进行调用 -> 占内存空间较大

  • 第 2 种是传【址】进行调用 -> 占内存空间较小,最好用这一种

  • 而第 2 种有可能会误改了结构体中的数据,如果不想被更改,可以在形式参数位置加上const

    Eg:

    void print2(const struct AA* p)
    {
    int i = 0;
        for( i = 0; i < 3 ; i ++ )
        {
            printf("%d", p -> date[i] );
        }
         printf("%d\n", p -> num);
    }
    

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

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

相关文章

[论文阅读]: Detecting Copyrighted Content in Language Models Training Data

发布链接&#xff1a;http://arxiv.org/abs/2402.09910 核心目标&#xff1a;检测语言模型的训练过程中是否使用了受版权保护的内容 基于假设&#xff1a;语言模型有可能识别训练文本中的逐字节选 工作&#xff1a;提出了 DE-COP&#xff0c;一种确定训练中是否包含受版权保…

如何在Android平板上使用谷歌浏览器进行网页缩放

在使用Android平板时&#xff0c;我们经常会浏览各种网页&#xff0c;但有时网页内容可能无法适应屏幕大小&#xff0c;这时就需要用到网页缩放功能。本文将为您详细介绍如何在Android平 板上的谷歌浏览器中进行网页缩放&#xff0c;帮助您更好地浏览网页。&#xff08;本文由h…

Cursor 平替项目 bolt.new

Cursor 是一个全新的编程工具&#xff0c;旨在帮助开发者更高效地写代码。它不仅能提升编程速度&#xff0c;还能让代码更干净、更智能。无论你是编程新手还是经验丰富的开发者&#xff0c;Cursor AI都能为你提供智能辅助&#xff0c;显著提高编程效率。 但是目前 Cursor 免费…

QT开发--文件的读写操作

第十三章 文件的读写操作 Qt提供两种读写纯文本文件的方法&#xff1a; 1、直接使用 QFile 类的IO功能&#xff1b; 2、结合 QFile 和 QTextStream&#xff0c;利用流(Stream)进行操作。 13.1 文件读操作 13.1.1 使用QFile类 Qt封装了QFile类&#xff0c;方便我们对文件进行操…

物联网直播技术揭秘:如何保证超高可用性?

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货! Hello,大家好!我是小米,一个29岁超爱分享技术的码农。今天跟大家聊一聊物联网时代下直播高可用方案的那些事儿。 随着物联网的快速发展,直播技术已…

针对考研的C语言学习(循环队列-链表版本以及2019循环队列大题)

题目 【注】此版本严格按照数字版循环队列的写法&#xff0c;rear所代表的永远是空数据 图解 1.初始化部分和插入部分 2出队 3.分部代码解析 初始化 void init_cir_link_que(CirLinkQue& q) {q.rear q.front (LinkList)malloc(sizeof(LNode));q.front->next NULL…

【宝可梦】游戏

pokemmo https://pokemmo.com/zh/ 写在最后&#xff1a;若本文章对您有帮助&#xff0c;请点个赞啦 ٩(๑•̀ω•́๑)۶

LeetCode讲解篇之1749. 任意子数组和的绝对值的最大值

文章目录 题目描述题解思路题解代码题解链接 题目描述 题解思路 这个我只需要求子数组和的最小值相反数和子数组和的最大值&#xff0c;本题答案为二者的最大值 设数组maxDp中第i号元素表示以nums[i]为结尾的子数组和的最大值 设数组minDp中第i号元素表示以nums[i]为结尾的子…

机器学习课程学习周报十六

机器学习课程学习周报十六 文章目录 机器学习课程学习周报十六摘要Abstract一、机器学习部分1. 再探马尔可夫链1.1 离散状态马尔可夫链1.1.1 转移概率矩阵和状态分布1.1.2 平稳分布 1.2 连续状态马尔可夫链1.3 马尔可夫链的性质 2. 马尔可夫蒙特卡罗法2.1 基本想法2.2 基本步骤…

77.【C语言】文件操作(3)

目录 6.文件的顺序读写 1.几个顺序读写函数 1.fgetc函数 代码示例 代码改进 2.fputc函数 3.fputs函数 如果需要换行,应该写入换行符(\n) 4.fgets函数 1.读取单行字符串 2.读取多行字符串 6.文件的顺序读写 1.几个顺序读写函数 分组:(fgetc,fputc),(fgets,fputs),(f…

服务器数据恢复—Raid5阵列硬盘磁头损坏导致掉线的数据恢复案例

服务器数据恢复环境&#xff1a; 一台某品牌存储设备上有一组由10块硬盘&#xff08;9块数据盘1块热备盘&#xff09;组建的raid5阵列&#xff0c;上层部署vmware exsi虚拟化平台。 服务器故障&#xff1a; raid5阵列中两块硬盘对应的指示灯亮黄灯掉线。硬盘序列号无法读取&am…

【动手学深度学习】6.3 填充与步幅(个人向笔记)

卷积的输出形状取决于输入形状和卷积核的形状在应用连续的卷积后&#xff0c;我们最终得到的输出大小远小于输入大小&#xff0c;这是由于卷积核的宽度和高度通常大于1导致的比如&#xff0c;一个 240 240 240240 240240像素的图像&#xff0c;经过10层 5 5 55 55的卷积后&am…

D3.js(五):实现组织架构图

实现组织架构图 效果初始化组织机构容器并实现缩放平移功能效果源码 渲染节点效果源码 渲染连线效果源码 完整源码 效果 初始化组织机构容器并实现缩放平移功能 效果 源码 import {useEffect} from react; import TreeData from ./json/tree-data.json;interface ITreeConfig…

电子电气架构---汽车OEM敏捷式集成方案简介

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c;多看一眼都是你的不…

数据在内存中的存储【下】

三.浮点数在内存中的存储 我们常见的浮点数&#xff1a;3.14159&#xff0c;1E10等&#xff0c;浮点数家族包括&#xff1a;float&#xff0c;double, long double类型。浮点数表示的范围&#xff1a;float.h中定义。之前我们说过浮点数在内存中无法精确保存&#xff0c;那为什…

OKHTTP 如何处理请求超时和重连机制

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

动态规划练习一

一、动态规划基本思路 1、状态表示&#xff1a;dp[i]的含义是什么 在一维数组中一般是“以 i 位置为结尾 / 起点....” 2、状态转移方程&#xff1a;dp[i]等于什么 一般是要根据最近的一步分情况进行讨论得出。 3、初始化dp表边界情况&#xff1a;保证在循环填表时不越界 …

80.【C语言】数据结构之时间复杂度

目录 1.数据结构的定义 2.算法的定义 3.算法的效率 1.衡量一个算法的好坏的方法 例题:计算以下代码的循环次数 2.大O的渐进表示法 练习1:求下列代码的时间复杂度 练习2:求下列代码的时间复杂度 练习3:求下列代码的时间复杂度 练习4:求下列代码的时间复杂度 4.总结:计…

9.存储过程安全性博客大纲(9/10)

存储过程安全性博客大纲 引言 在数据库系统中&#xff0c;存储过程是一种预先编写好的SQL代码集合&#xff0c;它被保存在数据库服务器上&#xff0c;可以通过指定的名称来调用执行。存储过程可以包含一系列的控制流语句&#xff0c;如IF条件语句、WHILE循环等&#xff0c;使…

SpringBoot项目-Thymeleaf安装

SpringBoot项目-Thymeleaf安装 参考文章:SpringBoot 整合Thymeleaf教程及使用方法 参考视频:模板引擎Thymeleaf快速入门 其实,参考的文章和视频,他们丢失了一些细节,我搞的时候还是有错 第1步:pom.xml增加依赖 <!-- SpringBoot集成thymeleaf模板 --><depe