• 【C语言】结构体、位段、枚举、联合(共用体)


    结构体

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

    结构体声明:struct是结构体关键字,结构体声明不能省略struct;

    匿名结构体:只能在声明结构体的时候声明结构体成员,之后再想用这个结构体声明结构体成员时就不行了;因为匿名结构体只能用一次,没有结构体名字没有办法声明结构体成员;(只打算用一次的结构体)


     结构体变量的声明与初始化

    结构体变量的初始化需要使用大括号进行初始化;

    1. // 结构体成员的声明与初始化
    2. struct student
    3. {
    4. // 声明的是一个描写学生的结构体
    5. char name[20];
    6. int age;
    7. char sex[5];
    8. } s1 = { "张三", 18, "男" }; // 声明的是结构体成员并初始化;
    9. int main()
    10. {
    11. struct student s2 = { "李四", 19, "女" }; // 声明的是结构体成员并初始化;
    12. struct student s3 = { .age = 20, .name = "王五", .sex = "男" }; // 指定初始化结构体成员变量,通过 . 指定;
    13. // 如果不指定初始化结构体成员变量,就顺序初始化;
    14. printf("%s %d %s\n", s1.name, s1.age, s1.sex);
    15. printf("%s %d %s\n", s2.name, s2.age, s2.sex);
    16. printf("%s %d %s\n", s3.name, s3.age, s3.sex);
    17. return 0;
    18. }

     结构的自引用

    可以包含同类型的结构体指针,但是不能包含同类型结构体;

    1. #include <stdio.h>
    2. struct Node
    3. {
    4. int data; // 存储数据 -> 数据域
    5. struct Node* n; // 存放下一个节点的地址 -> 指针域
    6. // 可以包含同类型结构体指针,但是不能包含同类型结构体
    7. };

    结构体内存对齐

    怎么对齐:

    1、第一个成员在结构体偏移量为0的地址处存放;

    2、其他成员变量要对齐到对齐数的整数倍的地址处;

    3、对齐数:VS默认对齐数、结构体成员自身大小就是对齐数,取较小的对齐数

    4、结构体总大小为最大对齐数的整数倍(每个成员都有一个对齐数),所有成员中的最大对齐数

    5、嵌套结构体的对齐,嵌套的结构体要对齐到自身最大对齐数的整数位;结构体的大小就是所有成员中的最大对齐数的整数倍

    为什么对齐:

    1、不是所有硬件平台都能访问任意地址上的数据的,有些只能在特定位置处取某些特定数据;

    2、结构体(尤其是栈)应该尽可能在自然边界上对齐;未对齐的内存,处理器需要访问两次;而对齐的只需访问一次;

    3、结构体的内存对齐就是拿空间换取时间的;

    如何节省空间:让占用空间小的成员集中在一起;

    代码验证: 
    1. #include
    2. int main()
    3. {
    4. struct S
    5. {
    6. char c1;
    7. int a;
    8. double b;
    9. char c2;
    10. }s;
    11. struct S1
    12. {
    13. int a;
    14. char c1;
    15. char c2;
    16. double b;
    17. }s1;
    18. printf("%d\n", sizeof(s));
    19. printf("%d\n", sizeof(s1));
    20. return 0;
    21. }

    百度笔试题: 

     写一个宏,计算结构体中某变量相对于首地址的偏移(offsetof宏);

    1. #include <stdio.h>
    2. #define offsetof(type, member) (size_t)&((type*)0->member)
    3. struct S
    4. {
    5. int a; // 第一个成员变量就在0地址处
    6. double b;
    7. }s;
    8. int main()
    9. {
    10. size_t offset = offsetof(s,b); // s-表示的是结构体的类型;b-表示的是b这个成员变量这个地址处的偏移量
    11. printf("b处的偏移量为:%zd", offset);
    12. }

      offsetof宏的使用:

    1. #include
    2. #include
    3. int main()
    4. {
    5. struct S
    6. {
    7. char c1;
    8. int a;
    9. double b;
    10. char c2;
    11. }s;
    12. struct S1
    13. {
    14. int a;
    15. char c1;
    16. char c2;
    17. double b;
    18. }s1;
    19. // offsetof宏:结构体成员变量相对于首地址的偏移量
    20. printf("%d %d %d %d\n", offsetof(struct S, c1), offsetof(struct S, a), offsetof(struct S, b), offsetof(struct S, c2));
    21. printf("%d %d %d %d\n", offsetof(struct S1, a), offsetof(struct S1, c1), offsetof(struct S1, c2), offsetof(struct S1, b));
    22. printf("%d\n", sizeof(s));
    23. printf("%d\n", sizeof(s1));
    24. return 0;
    25. }

    结构体传参:

    1、传值调用,形参相当于一份临时拷贝,修改形参不会改变实参;

    2、传址调用,如果不加上const,修改新参就会改变实参;

    3、传址调用比传值调用更节省空间,结构体传参尽量传址;


    位段

    1、设计位段的目的就是为了节省空间;

    2、位段的成员必须是int、unsigned int、signed int(c99之后,其他类型也可以,但是基本都是int、char类型);

    3、位段的成员名后边有一个冒号和一个数字(数字表示几个比特位);

    4、位段:为表示二进制位;

    1. #include
    2. int main()
    3. {
    4. struct S1 // 结构体
    5. {
    6. int a;
    7. int b;
    8. char c;
    9. };
    10. struct S2 // 位段
    11. {
    12. int a : 2;
    13. int b : 4;
    14. char c : 1;
    15. };
    16. printf("%d\n", sizeof(struct S1));
    17. printf("%d\n", sizeof(struct S2));
    18. return 0;
    19. }

     位段的内存分配

    1、不知道是从低位开始分配还是从高位才是分配,这是不确定的;


     位段的跨平台问题:

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

    2、位段中最大位的值是不确定的;

    3、位段中的成员在内存中,从右到左还是从左到右分配标准未定义;

    4、一个结构包含两个位段,第二个位段无法容纳于第一个位段剩余的位时,是舍弃位还是利用,是不确定的;


    枚举

    枚举类型的定义:

    1、把可能的取值一一列举(月份、星期);

    2、必须包含关键字enum;

    3、枚举的值是默认从0一次递增的;

    4、枚举常量的默认值是可以修改的;

    1. #include <stdio.h>
    2. enum Color
    3. {
    4. RED, // 注意,这是逗号,不是分号
    5. GREEN,
    6. BLUE // 最后这里什么符号都没有
    7. };
    8. enum Sex
    9. {
    10. MALE = 2, // 枚举常量的默认值是可以修改的
    11. FEMALE = 4,
    12. CECRECY = 1
    13. };
    14. int main()
    15. {
    16. printf("%d\n", RED);
    17. printf("%d\n", GREEN);
    18. printf("%d\n", BLUE); // 打印枚举常量
    19. printf("%d\n", FEMALE);
    20. return 0;
    21. }

     枚举的优点:

    1、枚举可以增加代码的可读性和可维护性;

    2、#define是用于替换的,没有类型的,但枚举有类型检查;

    3、可以一次定义多个常量;


    联合(共用体)

    1、联合定义的变量包含一系列的成员;

    2、这些成员共用同一块空间,所以也叫做共用体;

    3、在同一时间段内,联合体内的成员不能同时使用,只能使用其中一个;

    4、如果修改联合体内其中一个成员的值,则另一个的值也会改变;

    5、联合体的关键字:union;

    1. #include
    2. union Un
    3. {
    4. int a;
    5. char b;
    6. };
    7. int main()
    8. {
    9. union Un un;
    10. un.a = 10;
    11. un.b = 'a';
    12. printf("%d\n", sizeof(un)); // 大小是成员中较大的那个大小
    13. printf("%p\n", &un);
    14. printf("%p\n", &(un.a));
    15. printf("%p\n", &(un.b)); // 说明联合体共用一块空间
    16. return 0;
    17. }

     联合体大小的计算

    1、联合体的大小是最大成员的大小,这句话是错的;

    2、联合体的大小至少是最大成员的大小;

    3、当最大成员大小没有对齐到最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍处;

    1. #include <stdio.h>
    2. union Un
    3. {
    4. short a[7]; // 对齐数 2
    5. int b; // 对齐数 4
    6. // a占14个字节
    7. // 但是不是最大对齐数的整数倍
    8. // 所以联合体的大小是16
    9. }un;
    10. int main()
    11. {
    12. printf("%d\n", sizeof(un));
    13. return 0;
    14. }
  • 相关阅读:
    皕杰报表使用字体和部署后添加字体
    21 移动网络的前世今生
    优思学院|精益生产与柔性制造:现代制造业的双重理念
    Redis系列——什么是Redis
    Premiere Elements 2024(PR简化版)直装版
    java游戏制作-拼图游戏
    YOLO V3详解
    编程之路22
    SQL注入的一些注入
    论文解读(GMI)《Graph Representation Learning via Graphical Mutual Information Maximization》
  • 原文地址:https://blog.csdn.net/2201_75406088/article/details/133774117