0


struct结构体你了解多少,希望认真的6个小时,可以换来各位看官你的回眸

今天重点说下结稍微构体相关知识,今天内容有点多,荤素搭配,希望各位看官耐心看完,相信会有收获的 。
今天也要努力学习啊。 ღ( ´・ᴗ・` )
今天也要努力学习呀!

1.什么是结构?

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

2. 结构体都有“名字”吗

一般结构体就是匿名和非匿名,下面看看他们的区别吧
例 1:

structstu{int age;char ID[20];char name[20];}n1,n2,*n3;

这是正常结构体,内部是结构体成员,n1,n2是结构体创建的变量,n3时结构体变量指针,stu是结构体名称,也就是结构体名字。

那如果没有名字呢?
例 2:

struct{int age;char ID[20];char name[20];}n4;

这里的结构体就没有名字,只有一个结构体创建的变量n4,这叫匿名结构体。

那么有个问题来了,n4和n1或n2的类型相同吗?
请看下面代码
例 3:

structstu{int age;char ID[20];char name[20];}n1,n2,*n3;struct{int age;char ID[20];char name[20];}n4;intmain(){
    n3 =&n4;// err:“=”: 从“*”到“stu *”的类型不兼容return0;}

这样来看是不对的,虽然结构体成员一样,但类型是不同的。

3.结构体的自引用(类似链表)

一个结构体可以自己引用自己吗?
例 4:

structstu//err{int num;structstu next;};

这样定义结构体,然后自引用可以吗?
答案是不行,这样会有一个著名的先有鸡还是先有蛋的问题。

正确的方法还是通过指针进行自引用。

structstu{int num;structstu* next;};

这样通过指针的方式进行访问是可以

例 5:

typedefstruct//err{int data;
 Node* next;}Node;

typedef对类型重命名后的也不行哦
这样时不可以的,还是存在鸡蛋悖论。

应该改为这样

typedefstructNode{int data;structNode* next;}Node;

4.结构体的初始化

结构体初始化问题,一般来说,定义结构体变量时就要初始化了,这样方便快捷,否则就要一个个成员去一点点的进行初始化。
例 6:
在这里插入图片描述
这里用调试的方案来展现初始化的两种方法。分别是n1的先定义在逐步初始化,这里注意的是数组初始化要用strcpy函数进行字符串拷贝。n2是直接进行定义并初始化。比较来看,n2比n1要简单,快捷。各有各的优劣吧,怎样初始化相信各位看官已经明白了吧。

还有就是结构体嵌套初始化,起始大同小异。
例 :7
在这里插入图片描述

这样就可以进行嵌套结构体的初始化。
(同样你也可以通过访问一个个结构体成员变量的形式进行初始化)。

5.如何访问结构体成员

访问结构体成员一般有两种方式
1.通过 ‘ . ’点操作符进行访问,下面打印下例7,来更深刻认识下点操作符。
例 8:

structdate{char name[20];int age;}n6;structstu{int class;int grade;structdate n6;};intmain(){structstu n5 ={1,1,{"胡杨树下",16}};printf("%d\n", n5.class);printf("%d\n", n5.grade);printf("%s\n", n5.n6.name);printf("%d\n", n5.n6.age);return0;}

通过 点操作符 来进行访问结构体变量进行打印,可以访问到每个成员。

2.通过 -> 指向指针的方式进行打印,也可以的
例 9:

structdate{char name[20];int age;}n6;structstu{int class;int grade;structdate n6;};intmain(){structstu n5 ={1,1,{"胡杨树下",16}};structstu*n7 =&n5;printf("%d", n7->class);printf("%d", n7->grade);printf("%d", n7->n6.age);printf("%d", n7->n6.name);return0;}

这样通过指针的形式访问到了每个变量,也可进行打印。

6.结构体传参

那么各位读者大人,有想过为啥要有两种访问方法吗,一种不可以吗?
一种访问方式也不是不可以,只不过有时候为了效率更快,才用的两种方法,就比如说,一个结构体如果要传作为函数参数传递时,为了更快,更省空间,所以用传地址的形式,那么此时如果没有指针访问的形式,那么是不是很苦恼呢。
一般情况下结构体比正常定义的变量要大,传整体更费劲,更慢,更占空间所以一般传地址过去,这也就和上面说的指针访问结构体有联系,也回答了为什么有两种访问方法这一说了。

7.结构体内存对齐

为什么要有内存对齐呢?

  1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访 问。总体来说,就是为了快,可以浪费点空间。

内存对齐的规则

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

这个的详解可以看这位大佬的,很详细啦,就不在赘述了。
(最大对齐数是可以修改的)

#pragmapack(1)//在这之间的默认对齐数是1.#pragmapack()//取消设置的默认对齐数,还原为默认

结构体内存对齐数

8.空结构体大小问题

空结构体是多大,这个要看编译器,不同编译器不同效果。
在VS2013中,不允许定义的。
例 10:

structstu{};intmain(){int ret =sizeof(structstu);//error要求一个结构或联合至少有一个成员return0;}

在VS2013中结构体没有成员是不可以定义的。
在linux环境中,空结构体可以定义的,空结构体大小为0。

9 .结构体中的柔性数组概念(C99引入的)

柔性数组一般是在结构体中,用动态开辟的,一般柔性数组前必须要有一个有效的结构体成员,并且柔性数组成员是结构体最后一个成员。
例 11

structstr{int num;int arr[0];};intmain(){structstr*p =(structstr*)malloc \
    (sizeof(structstr)+sizeof(int)*10);return0;}

这里数组的数组,写成int arr[0] 或int arr[ ]都可以。
柔性数组的大小不算结构体的大小.
例 12:

structstr{int num;int arr[0];};intmain(){int ret  =sizeof(structstr);//结果是4return0;}

怎样访问柔性数组?
例 13:

structstr{int num;int arr[0];};intmain(){structstr*p =(structstr*)malloc \
    (sizeof(structstr)+sizeof(int)*10);for(int i =0; i <10; i++){
        p->arr[i]= i;}for(int i =0; i <10; i++){printf("%d ", p->arr[i]);}free(p);
    p =NULL;return0;}

运行结果
在这里插入图片描述

同样的结构体中的柔性数组会内存对齐吗?

实际上柔性数组,不完全算是结构体成员,它只是把地址交给结构体,让结构体使用,所以没有内存对齐这一说。

10.补充知识 : 位段

  1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

看个代码,了解下位段

例 14:

structS{char a :3;char b :4;char c :5;char d :4;};intmain(){structS s ={0};
    s.a =10;
    s.b =12;
    s.c =3;
    s.d =4;return0;}

在这里插入图片描述

从这里可以看处这上面的位段占用三个字节。

在这里插入图片描述
下面的进行的是赋值。这是在内存中的数字。

关于位段还有很多不确定的因素。

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。所以位段跨平台性很差。

下期预告

typedef和其他存储类型关键词

欢迎回访,欢迎指正。

标签: c语言

本文转载自: https://blog.csdn.net/m0_64770095/article/details/123932369
版权归原作者 沙漠下的胡杨 所有, 如有侵权,请联系我们删除。

“struct结构体你了解多少,希望认真的6个小时,可以换来各位看官你的回眸”的评论:

还没有评论