• C语言自定义类型一网打尽(结构体、位段/位域、枚举、联合体)


    前言

    C语言自定义类型有:结构体、枚举、联合体
    内置类型有:int、char、long、double、short、float等。

    结构体-struct

    结构体声明

    结构体 - 描述一个学生 名字,年龄,电话,性别
    定义 下面s1,s3为结构体全局变量。 struct Stu s2 = { "张三",20,"15129521207","男" }; s2就是正常局部变量的创建及初始化。

    struct Stu
    {
    	char name[20];
    	short age;
    	char tele[12];
    	char sex[5];
    }s1, s3;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    起别名:需要使用关键字typedef。下面就是将结构体Stu起了一个S的别名。之后就可以用别名代替结构体名。S s1;

    typedef struct Stu
    {
    	char name[20];
    	short age;
    	char tele[12];
    	char sex[5];
    }S;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    匿名结构体

    匿名结构体 只能·使用一次。编译器会将下面俩个当成不同结构体。

    // 匿名结构体类型
    struct
    {
    	int a;
    	float b;
    }x;
    
    // 匿名结构体指针
    struct
    {
    	int a;
    	float b;
    }*p;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    初始化

    普通结构体题初始化

    struct Stu
    {
    	char c;
    	int a;
    	double d;
    	char arr[20];
    };
    int main()
    {
    	struct Stu s = { 'c', 12, 3.6, "林夕" };
    	printf("%c %d %f %s\n", s.c, s.a, s.d, s.arr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    嵌套结构体类型

    struct Tea
    {
    	double weight;
    	short age;
    };
    
    struct Stu
    {
    	char c;
    	struct Tea st;
    	int a;
    	double d;
    	char arr[20];
    };
    
    int main()
    {
    	struct Stu s = { 'c',{120.6, 22}, 22, 5.6, "林夕" };
    	printf("%c %f %hd %d %f %s\n", s.c, s.st.weight, s.st.age, s.a, s.d, s.arr);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    结构体变量成员修改

    #include 
    #include 
    struct Book
    {
        char name[20];
        short price;
    };
     
    int main()
    {
        struct Book b1 = { "C++", 51 };
        strcpy(b1.name, "c"); // 结构体变量成员修改
        b1.price = 34; // 结构体变量成员修改
     
        printf("%s %d\n", b1.name, b1.price);
        printf("%u\n", sizeof(b1));//22
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    结构体内存对齐

    结构体对齐规则
    • 第一个成员在与结构体变量偏移量为0的地址处
    • 其他成员变量要对其到某个数字(对齐数)的整数倍的地址处。

    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值
    • VS中默认的值为8(gcc无默认对齐数,就按照该成员的大小)
    • 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
    • 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

    关于字节对齐:
    • 结构体各成员的起始位置相对于结构体变量的起始位置的偏移量,应该为该结构体成员类型所占字节数与pack(n)的n取最小值的倍数
    • 结构体变量所占字节数应该是结构体各成员所占字节数的最大值与pack(n)的n取最小值

    例子

    #include 
     
    struct S1
    {
        char c1;
        int a;
        char c2;
    };
     
    struct S2
    {
        char c1;
        char c2;
        int a;
    };
    int main()
    {
        struct S1 s1 = { 0 };
        printf("%u\n", sizeof(s1));//12
        struct S2 s2 = { 0 };
        printf("%u\n", sizeof(s2));//8
        return 0;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    为什么存在内存对齐?
    1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某个地址处取某些特定类型的数据,否则抛出硬件异常。(比如整型只能在4的倍数取出数据)
    2、性能原因:数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作俩次内存访问;而对齐的内存访问仅需要一次访问

    总体来说:结构体的内存对齐是拿空间来换取时间的做法。

    在这里插入图片描述
    修改默认对齐
    #pragma pack(8) 设置默认对齐数为8
    #pragma pack() 取消默认对齐数,还原为默认

    计算结构体所占字节数

    练习1:

    struct S1
    {
    char c1;
    int a;
    char c2;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    分析:
    最开始是c1字符类型,直接放
    a是整型,4字节与8字节相比,4小。所以要从地址4的倍数开始,与前面空三个,
    c2是字符型,4字节与8字节相比,1小,所以从地址1的倍数就是放,紧挨着上面放
    总大小是最大对齐数的倍数 最大对齐数为4 ,现在已经占了9个字节,所以总的大小就是12个字节。在下面又空三个字节
    在这里插入图片描述
    练习2:

    struct S2
    {
    	char c1;
    	char c2;
    	int a;
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    分析:c1占一个字节,c2也占一个字节,a是四个字节,所以先跨过俩个字节,然后占四个字节。
    在这里插入图片描述
    练习3:

    struct S2
    {
    	char c1;
    	char c2;
    	double a;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    分析:首先c1从开始占一个字节
    c2 也是一个字节 紧挨上面 也占一个字节
    a占8个字节,中间空六个字节,然后又占八个字节,
    总大小是最大对齐数, 最大对齐数为:8 刚好,所以就是16字节

    练习4:

    struct S3
    {
    	char c1;
    	char c2;
    	double a;
    };
    
    struct S4
    {
    	char c1;
    	struct S3 s3;
    	double d;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    分析:首先c1占一个字节,
    s3是一个结构体,该结构体内最大对齐数是8,与vs8的字节相比,就是8。所以先空7个字节,然后占用16个字节
    d是8个字节,前面刚好是8的倍数24,再占8个字节
    总大小是最大对齐数=8,前面共32 刚好是32.

    结构体传参

    原理:传值用 . 传地址用->
    例如下面这张图:
    在这里插入图片描述
    问题:上面函数那个好一点?
    答:首选2,因为传参需要压入栈中,如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销较大,所以会导致性能的下降。而传入指针是固定的4/8个字节

    offsetof结构体偏移量

    函数原型:size_t offsetof(strcut Name,memberName);
    头文件:#include
    看下面代码即懂。(不要在意头文件的位置)
    在这里插入图片描述

    位段/位域

    位段的声明和结构体是类似的,有俩个不同:
    • 位段的成员必须是int、unsigned int 或 signed int。(char也行)
    • 位段的成员名后面有一个冒号和一个数字。(int类型数字不能大于4*8 = 32)

    位段的内存分配
    1、位段的成员可以是int ,unsigned int ,signed int(有符号整形) 或者是 char(属于整形家族)类型
    2、位段的空间上是按照需要以4个字节(int)或者一个字节(char)的方式来开辟的。
    3、位段涉及很多不确定因素,段位是不跨平台,注重可移植的程序应该避免使用位段。
    例子:
    在这里插入图片描述
    内存结构:
    在这里插入图片描述
    位段的跨平台问题
    1、int位段被当成有符号数还是无符号数是不确定的
    2、位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32)
    3、位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
    4、当一个结构体含俩个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

    总结:跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

    位段的应用
    在这里插入图片描述

    枚举

    枚举类型的定义

    enum Sex
    {
    	MALE = 2,
    	FEMALE = 4,
    	SECRET = 8,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    优点如下:
    1、增加代码的可读性和可维护性
    2、和#define定义的标识符比较枚举有类型检查,更加严谨
    3、防止了命名污染(封装)
    4、便与调试(define会在预编译就处理好了。)
    5、方便使用,一次可以定义多个常量

    枚举的使用
    错误
    在这里插入图片描述

    联合(共用体)

    定义:联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员、特征是这些成员公用同一块空间

    在这里插入图片描述

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

    面试题:判断当前计算机的大小端存储。
    方法一:
    在这里插入图片描述

    方法二:
    在这里插入图片描述
    联合大小的计算
    • 联合的大小至少是最大成员的大小。
    • 当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍
    比如:

    在这里插入图片描述

  • 相关阅读:
    Activiti工作流学习笔记(四)——工作流引擎中责任链模式的建立与应用原理
    QTP——功能测试
    软件加密系统Themida应用程序保护指南(五):如何自定义对话框
    使用 AI 学习 Python web 的 django 代码(1/30天)
    web前端电影项目作业源码 大学生影视主题网页制作电影网页设计模板 学生静态网页作业成品 dreamweaver电影HTML网站制作
    torch.roll
    【面试题】详解Cookie、localStorage、sessionStorage区别
    java毕业设计基于互联网的图书管理系统—借阅管理子模块(附源码、数据库)
    MySQL之数据库三大范式
    Java知识总结
  • 原文地址:https://blog.csdn.net/qq_45254369/article/details/125981611