当前位置: 首页 > news >正文

C语言---自定义类型(上)(结构体类型)

结构体

        结构体的定义与声明

                结构体其实和数组一样,都是一些值的集合,只不过数组是一系类相同类型的值,而结构体里边的成员可以是不同的数据类型。

                关于它的声明,所用到的关键字是struct。

           声明的语法如下:

           struct 结构体名

          {

                成员变量列表;

           }变量名;

                我先举个例子,就比如一个学生有名字、年龄、成绩等信息。那么此时就可以用结构体来描述它。

struct Stu
{char name[20];//名字int age;double score;
};

        结构体变量的定义和初始化及其访问

                定义

                在刚才创建完成了结构体之后,我们就可以创建结构体的变量了。

                一共有三种方式:下面以代码加注释的方式给出。

struct Stu
{char name[20];//名字int age;double score;
}stu1;//在定义结构体的时候就创建的全局变量struct Stu stu2;//创建的全局结构体变量int main()
{struct Stu stu3;//创建的局部结构体变量return 0;
}

                初始化

             刚才上边的代码已经创建了几个Stu类型的结构体变量,现在我给结构体变量stu3初始化,代码如下

             //

             stu3 = {"xxc",18,99.0};//注意,我们给它初始化的时候需要按照顺序,如果你想要按照自己的顺序去初始化,那就要用到结构体成员访问操作符 ( . )

             stu3 = {.age = 20 , .name = "zhangsan" , .score = 98.5};

                访问结构体变量

                访问结构体的成员变量有两种方式,第一种比较的直接,我们用结构体变量.成员名的方式去访问结构体里的成员。第二种方式就要用到指针了,通过结构体指针->成员名的方式。代码如下:

#include<stdio.h>struct Stu
{char name[20];int age;float score;
};int main()
{//第一种方式打印struct Stu stu = { "zhangsan",21,98.4f };printf("%d %f\n", stu.age, stu.score);//第二种方式打印,先得到结构体的地址,再用指针的方式去打印struct Stu* p = &stu;printf("%d %f\n", p->age, p->score);return 0;
}

        嵌套结构体与匿名结构体类型

                嵌套结构体:

                嵌套结构体,顾名思义就是结构体充当了结构体的成员变量,请看下边的代码(同时,我会将访问其成员变量也附加进去):

//注意:以下的代码只是演示,没有什么实际的含义
#include<stdio.h>
struct id
{char ID[25];int x;
};struct Stu
{char name[20];struct id identity;
};int main()
{struct Stu stu1 = { "zhangsan",{"123456",2} };return 0;
}

                匿名结构体:

                匿名结构体就是结构体在创建的时候没给它名字,如下

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

           这样的话就不能创建结构体的变量了,只能用三种创建方法的第一种来创建,就是在定义的时候直接创建。并且你会发现当你取出他的地址赋值给一个匿名结构体类型的指针变量的时候是会失败的。因为C标准认为它们是两种不同的类型。所以往往匿名结构体类型只能用一次。

        结构体的内存对齐

                每一种数据只要存在在内存当中就肯定有大小,结构体也不例外,接下来所要讨论的就是结构体的内存大小,C语言给出了一套计算它大小的规则,我们称之为结构体的内存对齐。

           对齐规则:

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

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

                3.结构体的总大小为最大对齐数的整数倍

                接下来我们先来解释一下几个名词的含义:

                偏移量:我们可以借助offsetof宏来帮助我们理解,offsetof可以计算出结构体的成员相较于结构体起始位置的偏移量。offsetof有两个参数,第一个参数是结构体变量名字,第二个参数是结构体内部的成员名。  

                由上图我们可以知道每个结构体成员的偏移量是多少,如果你还没有概念,请不要着急,我会把所有名词解释完之后再次回过头来全部解释一下。

                对齐数: 在我们常见的编译器vs里边,默认的对齐数是8,但在计算结构体大小的时候,我们需要将默认的对齐数与该结构体成员变量的大小做比较,取较小的那个作为我们计算大小时候的依据。

                最大对齐数:结构体里边的每一个成员变量都有一个对齐数,它们之中最大的那个就叫做最大对齐数。

                了解了以上的规则与概念之后,我们再将上边截图里边的结构体拿出来,来根据规则计算一下它的大小。

                结构体嵌套结构体的内存对齐

                当我们想要计算的结构体里边有结构体嵌套的时候,它的大小又该如何计算呢?

                这时候,在上边规则的三条规则之下还有一条规则。

           4.当存在嵌套结构体的时候,该结构体成员对齐到自己内部成员的最大对齐数的整数倍处,结构体的大小就是全部成员(包括嵌套结构体的成员)的最大对齐数的整数倍。

                下边来看一个例子:

                修改默认对齐数:

                我们在上边已经提到过了,在vs里边的默认对齐数为8,当我们觉得这个默认对齐数不合适的时候,我们就可以用预处理指令#pragma来修改默认的对齐数。

#include<stdio.h>
#pragma pack(1)//设置默认对齐数为1
struct test
{char c1;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认对齐数为8的状态。
int main()
{struct test test2;printf("%d\n", sizeof(test2));return 0;
}

        结构体传参

                函数的传参分为两种,一种是传值,一种是传址。下边的代码将结构体的两种传参方式一并演示。

#include<stdio.h>
struct s1
{char a;int b;char c;
};void test1(struct s1 s)
{printf("test1的打印:%d\n", s.b);
}void test2(struct s1* ps)
{printf("test2的打印:%d\n", ps->b);
}int main()
{struct s1 sss;sss.b = 10;test1(sss);test2(&sss);return 0;
}

                这里再补充一个点,利用地址传参可以很好的提升程序的运行效率,因为如果是传值调用的话,程序会在内存里又开辟一块空间来临时拷贝实参。

        结构体实现位段

        首先明确位段在定义时候的两个注意点

        1.位段的成员必须是int,unsigned int,signed int,char,在C99中,位段成员的类型还可以是其他类型

        2.  位段的成员名后边有一个冒号和一个数字。

        如下就是一个基本的位段的定义

struct s
{int a : 2;int b : 4;int c : 6;
};

                位段的内存分配:

                在定义位段的时候,位段的每一个成员的冒号后边的数字代表的是比特位的意思,也就是在定义位段的成员变量的时候,已经把它所占的空间大小也限定住了。

                位段的空间是按照4个字节或者1个字节的大小来开辟空间的。还有一个值得注意的点就是位段不具有移植性,当需要跨平台使用的不推荐使用位段。

                接下来用一个例子来说明位段的内存分配:

                由以上的例子可见,位段的作用本质上还是在节约内存空间的消耗。

http://www.dtcms.com/a/277970.html

相关文章:

  • Vue Router 完全指南:从入门到实战,高效管理前端路由
  • C++高频知识点(十二)
  • 【LeetCode数据结构】单链表的应用——反转链表问题、链表的中间节点问题详解
  • 通信原理与USRP :PSK的调制解调(BPSK、QPSK、16PSK) 文本、图片
  • Struts2框架对重定向URL处理不当导致的OGNL注入漏洞(s2-057)
  • 【LeetCode 热题 100】105. 从前序与中序遍历序列构造二叉树——(解法二)O(n)
  • SSE连接错误机制处置
  • lvs负载均衡实操模拟
  • docker高级管理——Compose容器编排与私有仓库
  • 基于YOLOv11的无人机目标检测实战(Windows环境)
  • 小程序部分pai
  • 深度剖析:自定义线程安全 ppp::function 实现 vs std::function
  • 黑洞是什么?
  • 苍穹外卖Day3
  • python kivy 打包apk
  • 基于手势识别完成ESP32C3控制8位继电器实现智能鱼缸整体方案设计
  • “找到一个或多个多重定义的符号“(LNK2005 或 LNK1169)
  • JAVA 反射总结
  • 开源工具DeepFilterNet:实时语音降噪
  • SQL的初步学习(二)(以MySQL为例)
  • 第四章 城市给水排水处理厂站工程
  • 双向链表:前后遍历的艺术
  • 动态规划题解_将一个数字表示成幂的和的方案数【LeetCode】
  • 高压空气冲击炮cad【3张】三维图+设计说明书
  • Python 学习之路(十)--常见算法实现原理及解析
  • 智慧公安信息化建设解决方案PPT(63页)
  • Matlab的命令行窗口内容的记录-利用diary记录日志/保存命令窗口输出
  • 什么是 MVP?产品从0到1的关键一步
  • OSPF 基础实验
  • X00211-基于残差edge-graph注意力机制的深度强化学习优化车辆路径问题