当前位置:网站首页 > 技术博客 > 正文

c语言数据结构知识点



dcebcb7976884652a4fdad7708b196ea.png

目录

💕1.什么是结构体类型

💕2.结构体变量的创建

 💕3.结构体变量的初始化

💕4.结构体的特殊声明

💕5.typedef重定义结构体变量(两种方式)

 💕6.结构体自引用

💕 7.创建结构体指针变量

💕8.结构体内容的访问

8.1直接访问:

8.2 结构体指针访问

💕9.结构体内存存储 

9.1 结构体内存存储4条规则

例题1&&讲解:

结构体内容的储存方式:

结构体字节的大小:

 例题2&&讲解:

结构体内容的储存方式:

结构体字节的大小:

例题3&&讲解:

 结构体内容的储存方式:

结构体字节的大小:

例题4&&讲解(结构体嵌套问题 )

 结构体内容的储存方式: 

结构体字节的大小:

✨ 10.为什么存在内存对齐?

✨ 11.如何节省结构体内存大小

 ✨12.修改默认对齐数

✨ 13.scand初始化结构体变量

 错误写法如下​编辑:

✨14.结构体传参

​编辑


小心!VS2022不可直接接触,否则!没这个必要,方源面色淡然一把抓住!顷刻炼化! 


什么是结构体类型,这里拿一个常见的格式举例:

 
     

 这是一个很常见的结构体格式

struct stu 为结构体类型

s1 为结构体的变量

int a 为结构体变量的内容


结构体变量的创建有两种方法,一种是随着结构体的创建而创建

另一种则是单独创建,示例如下:

方法一:

 
      

 方法二(含注意事项):

 
       

 这里采用的是 用结构体类型创建结构体变量

不理解的可以类比int a,类型加变量名


 结构体变量的初始化可以分为三种情况:


情况1&&情况3:
    变量随着结构体而定义
    定义结构体变量需要采用,变量名.结构体内容 进行初始化结构体内容

 
        

 情况2:
变量不随着结构体而定义
定义结构体变量采用, 结构体类型+变量名 的形式进行初始化结构体内容

 
        
       
      

如果结构体声明不完全,创建的结构体只能使用一次,示例如下:

 
       

 第一种方式:

 
         

第二种方式:

 
         
        


结构体自引用是一件很可怕的事,结构体最怕的就是重命名,一旦重命名就会出现错误

比如说结构体的变量与结构体的内容重名就会出现错误

那么什么是结构体的自引用呢?结构体的自引用就是一个结构体嵌套自己的结构体

示例如下:

 
          

 使用自己的结构体嵌套自己,就会出现无数多个自己的结构体,结构体的内存就会无限大,是不合理的


对于结构体来说,结构体在未创建变量时不占用内存

所以结构体的变量名会表示整个结构体,因此&结构体变量名,就相当于取出了整个结构体变量的内存

那么如何创建结构体指针变量呢?示例如下:

 创建结构体指针变量第一种写法:

 
           

 相信有的同志会写出这样的代码:

 
           

其实这样写也是对的,因为在结构体大括号外的 ; 写完之后,再写句子就是单独的表达式了,所以其实和第一种写法是完全相同的,只是代码放的位置不一样

创建结构体指针变量第二种写法:

 
          


结构体内容的访问可分为两种,一种为直接访问,另一种为通过结构体指针访问

 
            
 
           

结构体内存储存方式有以下4条规则,也叫做结构体内存对齐原则
1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数    = 编译器默认的第一个对齐数与该成员变量大小的较小值
vs中默认的值为8
Linux中gcc没有默认对齐数,对齐数就是成员自身的大小
3.结构体的总大小为最大对齐数(结构体中每个成员都有一个对齐数,所有对齐数中最大的)的整数倍
4.如果嵌套了结构体,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍,结构体的整数大小就是所有最大对齐数(含嵌套结构体中成员的对齐数的整数倍)






多说无益,我们拿 代码例题+配图 来讲解

 
            

首先结构体的内存存储默认从0开始存储,我们以例题讲解

结构体内容的储存方式:

第一个结构体内容为char c1,结构体成员 c1 占用1个字节大小,结构体默认从0位置开始存储,那就从0开始存储1个字节大小的内存

第二个结构体内容为int i,结构体内容 i 占用4个字节,但因为结构体内存对齐原则,占用4个字节的int i,只能从 4 的整数倍数处(也叫结构体成员 i 的对齐数的整数倍)开始存储内存,所以内存将拓展到 4 的位置,从4的位置开始向下存储4个字节大小,4 5 6 7都是 i 的内存存储处

第三个结构体内容为char c2,结构体成员 c2 占用一个字节,因为结构体内存对齐原则,占用1个字节大小的char c2只能从1 的整数倍数处(对齐数的整数倍)开始存储内存,所以接着从 7 的位置向下存储1个字节大小的内存,即:将8的位置设为char c2 的内存存储处


结构体字节的大小:

在我们清楚完毕结构体内存的存储位置后,因为结构体的总大小为最大对齐数(结构体中每个成员都有一个对齐数,所有对齐数中最大的)的整数倍,在此例题中结构体成员的最大对齐数为4,也就是int i 中的 i 的对齐数为4,

因为结构体内存存储占用了9个字节的大小,所以我们取4的整数倍数(最大对齐数)12,最终大小被填充到12个字节

7c148244ec734f7f8649a743576f0a03.jpeg

 
             

结构体内容的储存方式:

第一个结构体内容为char c1,结构体成员c1占用1个字节大小,结构体默认从0位置开始存储,那就从0开始存储1个字节大小的内存

第二个结构体的内容为char c2,结构体成员c2占用1个字节,但因为结构体内存对齐原则,占用1个字节的char c2,只能从 1 的整数倍数处(也叫c2 的对齐数的整数倍)开始存储内存,所以向下1个字节的位置为c2的内存存储地址

第三个结构体内容为int i,结构体成员 i 占用4个字节,因为结构体内存对齐原则,占用4个字节大小的int i 只能从4 的整数倍数处(对齐数的整数倍)开始存储内存,所以我们从 4 的整数倍处开始存储4个字节,4 5 6 7都为结构体变量内容 i 的存储地址

结构体字节的大小:

在我们清楚完毕结构体内存的存储位置后,因为结构体的总大小为最大对齐数(结构体中每个成员都有一个对齐数,所有对齐数中最大的)的整数倍,在此例题中结构体成员的最大对齐数为4,也就是int i 中的 i 的对齐数为4,

因为结构体内存存储占用了8个字节的大小,所以我们取4的整数倍数(最大对齐数)8,最终结构体字节大小为8个字节

45adfa2174674b4da65667ef24817862.jpeg

 
             

 结构体内容的储存方式:

第一个结构体内容为double d,结构体成员 d 占用8个字节大小,结构体默认从0位置开始存储,那就从0开始存储8个字节大小的内存

第二个结构体的内容为char c,结构体成员 c 占用1个字节,但因为结构体内存对齐原则,占用1个字节的char c,只能从 1 的整数倍数处(也叫c 的对齐数的整数倍)开始存储内存,所以向下1个字节的位置为 c 的内存存储地址

第三个结构体内容为int i,结构体成员 i 占用4个字节,因为结构体内存对齐原则,占用4个字节大小的int i 只能从4 的整数倍数处(对齐数的整数倍)开始存储内存,所以我们从 4 的整数倍处开始存储4个字节,因为8的位置已经被存储了,所以我们从下一个整数倍数--也就是12的位置开始存储,12 13 14 15都为结构体变量内容 i 的存储地址

结构体字节的大小:

在我们清楚完毕结构体内存的存储位置后,因为结构体的总大小为最大对齐数(结构体中每个成员都有一个对齐数,所有对齐数中最大的)的整数倍,在此例题中结构体成员的最大对齐数为8,也就是double d 中的 d 的对齐数为4,

因为结构体内存存储占用了16个字节的大小,所以我们取最大对齐数 8 的整数倍数(最大对齐数)16,最终结构体大小为16个字节

58c6dc6b4839405a99b665ff8f6908cb.jpeg

 
             

 结构体内容的储存方式: 

第一个结构体内容为char c1,结构体成员 c1 占用1个字节大小,结构体默认从0位置开始存储,那就从0开始存储1个字节大小的内存

第二个结构体的内容为struct S3 s3,结构体成员 s3 占用16个字节,但因为是结构体嵌套的内存存储,所以我们要从s3中成员最大对齐数的整数倍处开始存储,s3中最大对齐数的整数倍为8,所以我们从8的位置开始往下存储16个字节,直到23

第三个结构体内容为double d,结构体成员 d 占用8个字节,因为结构体内存对齐原则,占用8个字节大小的double d 只能从8 的整数倍数处(也叫对齐数)开始存储内存,所以我们从 8 的整数倍处开始存储8个字节,也就是从24的位置开始,向下存放8个字节大小的内存

结构体字节的大小:

在我们清楚完毕结构体内存的存储位置后,因为结构体的总大小为最大对齐数(结构体中每个成员都有一个对齐数,所有对齐数中最大的)的整数倍,在此例题中结构体成员的最大对齐数为8,也就是double d 或者 s3中double d  的对齐数 8,

因为结构体内存存储占用了32个字节的大小,所以我们取最大对齐数 8 的整数倍数32,最终大小为32个字节

e5f1b12faf7345308f55da5b363ded9c.jpeg


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





想要节省结构体内存的大小,我们需要同时满足对齐,又要节省空间

我们可以通过将占用空间小的结构体成员尽量集中在一起

例如:

 
                

S1和S2的结构体成员一模一样,但是S2占用的字节比S1小


在结构体中,我们可以使用一个预处理指令来修改结构体的默认对齐数

 
                 

#pragma这个预处理指令可以改变编译器的默认对齐数,这对于节省结构体内存非常有用

#pragma改变默认对齐数后,所有结构体内容的对齐数都会被#pragma修改

使用示例如下:

 
                 

修改结构体默认对齐数后,所有结构体成员都会受影响,因此结构体成员的最大对齐数也会随之改变 

分析如图:

因为结构体成员的最大对齐数为1,所以结构体内存就以最大对齐数的整数倍(也就是 1 的整数倍)为结构体字节大小

027b6165cc294e1f84c61f8255f77f49.jpeg89d0313f7e134ed789740fc5df90ba1f.jpeg


使用scanf初始化结构体变量,就需要对变量内的内容直接访问,以初始化结构体变量内的内容

 
                  

d3a13b0f06af41bf8e7b82802130e757.png

使用&s1是不可以完成赋值结构体变量的内容的,因为结构体变量内存的存储方式多种多样,结构体变量的地址与结构体变量第一个内容的地址相同,但之后的变量内容怎么办?无法通过scanf一次性输入

所以不能使用&s1来实现结构体的输入

结构体传参传的是什么?

结构体传参有两种传法,一种是将整个结构体内容全部传过去,另一种则是将结构体变量的地址传过去

示例如下:

 
                  

注意:结构体传参时,结构体必须为全局变量,如果为局部变量,结构体传参就会报错,如下图:

cdc71467c76748bda13e385d47bc2ee4.png


问:print1 与 print2 哪个更好些?

答案是首选print2函数,传递结构体指针的地址更好

原因:函数传参时,参数是需要压栈的,会有时间和空间上的开销

如果我们将一整个结构体传过去,结构体的内存越大,参数压栈的系统开销就越大,会导致性能的下降

结论:结构体传参时,首选传递结构体的地址


  • 上一篇: java设置代理
  • 下一篇: 服务器硬件是什么
  • 版权声明


    相关文章:

  • java设置代理2025-07-29 12:30:01
  • 图像逆滤波2025-07-29 12:30:01
  • debian9源2025-07-29 12:30:01
  • 尺度空间app2025-07-29 12:30:01
  • 均值滤波算法2025-07-29 12:30:01
  • 服务器硬件是什么2025-07-29 12:30:01
  • 思科模拟器怎么给交换机配ip地址2025-07-29 12:30:01
  • 地理空间数据索引2025-07-29 12:30:01
  • 文件描述符号2025-07-29 12:30:01
  • phython入门2025-07-29 12:30:01