C语言自定义类型数据介绍

目录:

结构体 

结构体的声明

结构体的自引用

结构体变量的定义和初始化

结构体内存对齐

结构体传参

位段

什么是位段

位段的内存分配

位段的跨平台问题

枚举

枚举类型的定义

枚举的优点

枚举的使用

联合

联合类型的定义

联合的特点

联合大小的计算


正文开始:

1.结构体

1.1  结构体的基础知识

        结构体变量是一群不同的变量用来形容一个物体的一个组合变量,就像是我们描述一个学生,会描述他的姓名,年龄,学号,成绩等等,然而这些东西不是一个普通的变量就能全部描述的,我们就引进了结构体变量的概念

1.2  结构体的声明

  • 举个栗子
struct Stu
{
    char name[20];
    int year;
    int number;
    float score;
}stu1;

从上面的例子,我们可以总结出        

  • 结构体声明的大致模板为:
struct tag//tag是我们定义的结构体的类型名称,struct是结构体的关键字
{
    member-list;//成员列表,我们可以在内部自定义我们需要的变量类型
}variable-list;//这里的varibale-list可以省略,可以在我们需要使用的时候再进行定义

1.3  特殊的声明

  • 结构体在声明时可以省略掉tag,但是此时的variable-list就不能省略了,不然声明的这个结构体就用不了了
  • 对于匿名结构体,即使两个匿名结构体的成员变量完全一致,并且顺序都完全一致,也是不能把这两个结构体看作是同一个结构体

1.4  结构体的自引用

  • 在结构体中包含的一个类型为结构体本身的成员是不可以的,这样会出现问题,譬如sizeof(该结构体)不知道咋整了,最合理的是包含该结构体类型的指针,这就要涉及到链表的知识了,这里先不讲,留到后面再详细介绍
  • 结构体类型声明后想要定义一个对应类型的变量要写好大一串,如果不想的话,我们可以用到typedef
  • 举个栗子
typedef struct Node
{
    int data;
    struct Node* next;
}Node;
  • 此时分号;前面的就不是变量了,而是重命名的struct Node,后面想要定义struct Node类型是变量就可以将变量类型直接写成Node了

1.5  结构体变量的定义和初始化

  • 有了上面的结构体声明,再定义结构体变量就是很容易的事情了,就把结构体的声明当成一种数据类型,定义变量就和其他的普通变量定义是一样的,不过初始化就有所差异了
  • 初始化变量一般在定义时就会进行,因为结构体变量里面包含了多个不同类型的变量,所以在初始化时,先要用{},再将结构体变量的每一个成员变量的初始值给出,每个值用,隔开
  • 举个栗子
struct Stu //类型声明
{
    char name[15];//名字
    int age; //年龄
};
struct Stu s = {"zhangsan", 20};//定义变量的时候进行初始化

1.6  结构体内存对齐

  • 既然结构体变量是一种变量,自然也就有大小,但是结构体变量的大小不是能直接看出的,要通过内存对齐规则来进行计算
  • 先上文字描述
    • 结构体变量的第一个成员变量放在跟结构体地址偏移量为0的地址处
    • 接下来的变量需要比较对齐数与自身的大小,以两者中较小的作为对齐数,也就是从第二个变量开始,内个变量存放之前都要确定它的具体存放位置,而不是单纯的跟在上一个变量的结束位置处,变量存放的位置偏移量要是对齐数的整数倍,夹在两个变量存储内置未被使用的字节就被浪费掉了(VS的默认对齐数为8,Linux没有默认对齐数)
    • 结构体变量的大小总是最大对齐数的整数倍
    • 如果嵌套了其他结构体变量,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的大小就是所有最大对齐数(含嵌套的结构体的最大对齐数)的整数倍
  • 举个栗子

 代码实现如下

 总的来说,结构体的内存对齐就是典型的拿空间换时间的做法

1.7  修改默认对齐数

#pragma pack(4)//修改默认对齐数为4,一般来说,默认对齐数一般修改为2的整数次幂
#pragma pack()//取消设置的默认对齐数,还原为默认值

//在默认对齐数不合适的时候,可以修改默认对齐数

1.8  结构体传参

  • 这个涉及到压栈的问题,后面会专门写一篇文章讲相关内容,这里先跳过~
  • 总的来说就是结构体传参要传结构体的地址

2.位段

2.1 什么是位段

  • 位段的声明是与结构体类似的,但是它的成员变量只允许是int、unsigned int、signed int和char
  • 位段的成员变量后面有一个冒号和数字
  • 举个栗子

struct A 
{ 
    int _a:2; 
    int _b:5; 
    int _c:10; 
    int _d:30; 
};

那么,struct A 的大小是多少呢?

2.2 位段的内存分配

按照上图的分析,那么这个位段的大小就应该是8,事实是不是这样呢?

 代码实现如下

  •  结果跟我们所分析的是一样的,但是位段涉及很多不确定因素,这只是在VS下完全符合我们的分析

2.3 位段的跨平台问题

  • int 位段被当成有符号数还是无符号数是不确定的
  • 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题
  • 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义
  • 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的
总结:跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在

3.枚举

所谓枚举,顾名思义就是将所有的可能性全部一一列出,例如月份,星期几,性别,颜色等等

3.1 枚举类型的定义

enum Color//颜色
{
    RED,
    GREEN,
    BLUE
};
  • {}中的可能取值被称为枚举常量
  • 这些可能值是有值的,一般默认从0开始,而后的依次递增1,同时,这些可能指在被定义时可以给它赋初值
  • 举个栗子
enum Color//颜色
{
    RED=1,
    GREEN=2,
    BLUE=4
};

3.2 枚举的优点

  •  增加代码的可读性和可维护性
  • 和#define定义的标识符比较枚举有类型检查,更加严谨。
  • 防止了命名污染(封装)
  • 便于调试
  • 使用方便,一次可以定义多个常量

3.3 枚举的使用

enum Color//颜色
{
    RED=1,
    GREEN=2,
    BLUE=4
};
enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 5; //这样的非法的,不能将int类型的数据赋给枚举类型

4.联合(共用体)

联合体和结构体一样,包含一系列的变量,但不同的是,联合体里面包含的变量共用同一块空间(因此也被称为共用体)

4.1 联合体类型的定义

//联合类型的声明
union Un
{
    char c;
    int i;
};
//实际上变量c和变量i是放在内存中的同一位置的

4.2 联合的特点

  • 联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)

4.3 联合大小的计算

  • 联合的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
  • 举个栗子
union Un1
{
    char c[5];//占用内存字节为5,5<8,所以对齐数为5
    int i;//占用内存字节为4,4<8,所以对齐数为4
};
union Un2
{
    short c[7];占用内存字节为14,14>8,所以对齐数为8
    int i;占用内存字节为4,4<8,所以对齐数为4
};

printf("%d\n", sizeof(union Un1));//最大对齐数为5,就是5了
printf("%d\n", sizeof(union Un2));//最大对齐数为8,16是8的整数倍

以上就是本篇的所有内容啦,欢迎各位uu们的指正和建议~

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值