目录
  • 一、结构体
    • 1.结构体的声明
      • 局部结构体变量
      • 全局结构体变量
    • 2.特殊声明
      • 3.结构体的自引用
        • 4.结构体变量的初始化
          • 5.结构体内存对齐 
            • 6.修改默认对齐数
              • 7.结构体传参
                • 传址调用原因:
            • 二、位段
              • 举例:
                • 分析:
                  • 跨平台问题:
                  • 三、枚举
                    • 枚举类型的定义:
                      • 枚举的优点 
                      • 四、联合
                        • 1.联合类型的定义
                          • 2.联合的特点 
                            • 使用案例:
                            • 分析:
                          • 3.联合大小的计算 
                            • 举例:
                            • 分析:

                        一、结构体

                        结构体是不同类型变量的集合体

                        1.结构体的声明

                        struct Book      
                        {
                         char name[20];//名字
                         int Price;//价格
                         char Writer[5];//作者
                         char Time[20];//日期
                        };    //注意分号不能丢

                        struct为结构体关键字,Book为结构体标签,中间不同类型的变量为结构体的成员。上述现在只是定义了一个结构体类型struct Book。

                        局部结构体变量

                        int main()
                        {
                           struct Book B1; // B1为局部结构体变量
                           return 0;
                        }

                        全局结构体变量

                        struct Book      
                        {
                         char name[20];
                         int Price;
                         char Writer[5];
                         char Time[20];
                        }B3,B4,B5;      //在结构体类型后可连续定义多个全局结构体变量
                         
                        struct Book B2;   //B2为全局结构体变量
                         
                        int main()
                        {
                           return 0;
                        }

                        2.特殊声明

                        不完全声明

                        //匿名结构体类型--没有结构体标签
                        struct
                        {
                         int a;
                         char b;
                         float c;
                        }x;
                        //这样的结构体类型必须紧跟着定义结构体变量
                        //后面不能定义变量

                        不完全声明类型只能在定义使用一次,并且在vs中:

                        struct
                        {
                         int a;
                         char b;
                         float c;
                        }x;
                         
                        struct
                        {
                         int a;
                         char b;
                         float c;
                        }* ps;
                         
                        int main()
                        {
                          ps=&x;      //编译器默认两者类型不兼容  //且是错误写法
                          return 0;
                        }

                        C语言 自定义类型全面系统理解

                        因此不完全声明很少使用,不推荐。 

                        3.结构体的自引用

                        这里可以用链表的实现来理解:

                        C语言 自定义类型全面系统理解

                        struct Node
                        {
                         int data;
                         struct Node* next;  
                        };

                        C语言 自定义类型全面系统理解

                        这样就实现了自己类型的对象找自己类型对象的方法,这就是结构体的自引用。 

                        4.结构体变量的初始化

                        以上面struct Book为例:

                        struct Book      
                        {
                         char name[20];
                         int Price;
                         char Writer[5];
                         char Time[20];
                        }; 
                         
                         
                        int main()
                        {
                           struct Book B1={"三脚猫",50,“阿里”,“20081001”};
                           //初始化要用大括号
                           return 0;
                        }

                        嵌套结构体的初始化:

                        struct Data
                        {
                          int a;
                          char b[6];
                        };
                         
                        struct Book      
                        {
                         struct Data D;
                         char name[20];
                         int Price;
                         char Writer[5];
                         char Time[20];
                        }; 
                         
                         
                        int main()
                        {
                           struct Book B1={{4,"haha"},"三脚猫",50,"阿里","20081001"};
                           //大括号里加大括号
                           return 0;
                        }

                        5.结构体内存对齐 

                        计算结构体在内存中的大小

                        方法:

                        1. 第一个成员为起始,设从下标为0的地址开始向后存储。

                        2. 其他成员变量要对齐到对齐数的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数与 该成员大小的较小值。 VS中默认的值为8

                        3. 结构体总大小为所有成员对齐数中最大对齐数的整数倍。

                        4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

                        举例说明:

                        #include <stdio.h>
                        struct S1
                        {
                        	char c1;
                        	int i;
                        	char c2;
                        };
                         
                        struct S2
                        {
                        	char c1;
                        	char c2;
                        	int i;
                        };
                         
                        int main()
                        {
                        	printf("%d\n", sizeof(struct S1));
                        	printf("%d\n", sizeof(struct S2));
                        	return 0;
                        }

                        C语言 自定义类型全面系统理解

                        分析:

                        C语言 自定义类型全面系统理解

                        嵌套结构体

                        #include <stdio.h>
                        struct S1
                        {
                        	char c1;
                        	int i;
                        	char c2;
                        };
                         
                        struct S2
                        {
                        	char c1;
                        	char c2;
                        	struct S1 s1;
                        	int i;
                        };
                         
                        int main()
                        {
                        	printf("%d\n", sizeof(struct S1));
                        	printf("%d\n", sizeof(struct S2));
                        	return 0;
                        }

                        C语言 自定义类型全面系统理解

                        分析:

                        C语言 自定义类型全面系统理解

                        内存对齐的存在,在平台和性能两方面,可以使访问空间更加高效,用空间换取时间。

                        让占用空间小的成员尽量集中在一起。

                        6.修改默认对齐数

                        #pragma pack(1)     //修改默认对齐数为1
                         
                        //一般修改的对齐数为2^n

                        举例说明:

                        #include <stdio.h>
                        #pragma pack(1)   //修改为1相当于取消了对齐,没有优化,在实际应用中很少用
                        struct S1
                        {
                        	char c1;
                        	int i;
                        	char c2;
                        };
                        int main()
                        {
                        	printf("%d\n", sizeof(struct S1));  // 6
                        	return 0;
                        }

                        当默认对齐数被修改后,每个类型的对齐数都变为1,整体的最大对齐数也为1(相当于没有对齐),整体大小是1的倍数,则1+4+1=6。

                        C语言 自定义类型全面系统理解

                        7.结构体传参

                        当一个函数涉及到结构体时,最好用传址调用:

                        struct S
                        {
                         int data[1000];
                         int num;
                        };
                        struct S s = {{1,2,3,4}, 1000};
                        //结构体传参
                        void print1(struct S s)
                        {
                         printf("%d\n", s.num);
                        }
                        //结构体地址传参
                        void print2(struct S* ps)
                        {
                         printf("%d\n", ps->num);
                        }
                        int main()
                        {
                         print1(s);  //传结构体
                         print2(&s); //传地址
                         return 0;
                        }

                        传址调用原因:

                        1.函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。

                        2.如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。

                        二、位段

                        位段的声明和结构是类似的,有两个不同:

                        1.位段的成员必须是整型。

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

                        举例:

                        struct S
                        {
                         int _a:2;
                         int _b:5;
                         int _c:20;
                         int _d:25;
                        };

                        此时S就是一个位段类型

                        他的大小为8

                        printf("%d\n", sizeof(struct S));

                        C语言 自定义类型全面系统理解

                        分析:

                        下面我们来分析位段在内存中的存储:

                        C语言 自定义类型全面系统理解

                        注:若初始化的值大于给其指定的空间,则先会发生截断(断左取右),再进行存储

                        C语言 自定义类型全面系统理解

                        位段是根据实际需求来进行开辟空间,目的是为了节省空间提高效率。

                        跨平台问题:

                        1. int 位段被当成有符号数还是无符号数是不确定的。

                        2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机 器会出问题。

                        3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。(取决于编译器)

                        4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。

                        三、枚举

                        就是把可能的取值一一列举

                        枚举类型的定义:

                        例子:

                        enum Day  //星期
                        {
                         Mon,
                         Tues,
                         Wed,
                         Thur,
                         Fri,
                         Sat,
                         Sun
                        };

                        enum Day就是一个枚举类型,{}中的内容是枚举类型的可能取值,也叫枚举常量。

                        枚举常量是有值的,默认从0开始,依次递增。

                        int main()
                        {
                           enum Day d=Mon;   //定义一个变量,赋予{}内可能的取值。
                           return 0;
                        }

                        C语言 自定义类型全面系统理解

                        在定义的时候也可以赋初值,后面的常量依次递增一

                        C语言 自定义类型全面系统理解

                        枚举的优点 

                        1. 增加代码的可读性和可维护性

                        2. 和#define定义的标识符比较枚举有类型检查,更加严谨。

                        3. 防止了命名污染(封装)

                        4. 便于调试

                        5. 使用方便,一次可以定义多个常量

                        注:最好用枚举常量给枚举变量赋值,才不会出现类型上的差异。

                        举例:

                        当用cpp来运行程序时会报错,因为cpp对代码的格式会更加严格

                        C语言 自定义类型全面系统理解

                        而c就可以运行过去

                        C语言 自定义类型全面系统理解

                        养成良好的代码风格,做到认真严谨。

                        四、联合

                        联合也是一种特殊的自定义类型

                        1.联合类型的定义

                        //联合类型的声明
                        union Un
                        {
                         char c;
                         int i;
                        };
                        //联合变量的定义
                        union Un un;
                        //计算连个变量的大小
                        printf("%d\n", sizeof(un));

                        2.联合的特点 

                        联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小。

                        union Un
                        {
                        	int i;
                        	char c;
                        };
                        union Un un;
                         
                        int main()
                        {
                        	printf("%d\n", &(un.i));
                        	printf("%d\n", &(un.c));
                        	return 0;
                        }

                        C语言 自定义类型全面系统理解

                        共用一块空间,起始地址相同。

                        使用案例:

                        union Un
                        {
                        	int i;
                        	char c;
                        };
                        union Un un;
                         
                        int main()
                        {
                        	//printf("%d\n", &(un.i));
                        	//printf("%d\n", &(un.c));
                        	un.i = 0x11223344;
                        	un.c = 0x55;
                        	printf("%x\n", un.i);
                        	return 0;
                        }

                        C语言 自定义类型全面系统理解

                        分析:

                        C语言 自定义类型全面系统理解

                        3.联合大小的计算 

                        联合的大小至少是最大成员的大小。

                        当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

                        举例:

                        union Un1
                        {
                        	char c[5];
                        	int i;
                        };
                        union Un2
                        {
                        	short c[7];
                        	int i;
                        };
                        //下面输出的结果是什么?
                        int main()
                        {
                        	printf("%d\n", sizeof(union Un1));
                        	printf("%d\n", sizeof(union Un2));
                        	return 0;
                        }

                        C语言 自定义类型全面系统理解

                        分析:

                        C语言 自定义类型全面系统理解

                        声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。