云计算百科
云计算领域专业知识百科平台

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

目录

结构体

匿名结构体

结构体的自引用

结构体的内存对齐

对齐规则

结构体内存对齐的原因

结构体传参

位段

位段的声明

位段如何分配内存

位段的不确定因素

联合体

联合体的声明

联合体的大小

联合体的应用

枚举

枚举的声明

枚举的使用

枚举相比于宏定义“#define”的优点


结构体

结构体生活中一类实物抽象出来的一个定义,结构体的定义和结构体变量的创建以及初始化如下:

#include<stdio.h>

struct Std//结构体的创建
{
int age;
char name[];
};
int main()
{
struct Std s1;//结构体变量的创建
s1 = {18,"zhangsan"};//结构体变量的初始化
s1.age = 22;//可单独初始化
return 0;
}

匿名结构体

顾名思义:没有名字的结构体。(一般只能使用一次)在代码中的定义如下:

#include<stdio.h>

struct
{
int a;
char b;
}x;//创建匿名结构体变量x

结构体的自引用

结构体自己引用自己叫做结构体的自引用,(和二叉树的原理相似)在代码中的使用如下:

#include<stdio.h>
struct Std
{
int a;
struct Std* next;//将下一个同类型的结构体指针作为成员放在结构体中
};

结构体的内存对齐

对齐规则

  • 结构体第一个成员对齐距结构体变量起始位置偏移量为0地址处;
  • 后续成员对其到某一个对齐数的整数倍地址处;(对齐数为:系统默认对齐数和该成员自身对齐数的较小数;VS系统下默认对齐数为:8;Linux系统没有默认对齐数,所以对齐数为成员自身对齐数)
  • 结构体的整体内存必须是最大对其数的整数倍;
  • 若存在结构体嵌套的情况,嵌套的结构体对齐数为该结构体中最大对齐数,最终结构体的内存大小是说有对齐数中最大对齐数的整数倍(包含嵌套结构体中的对齐数)。
  • PS:函数“offsetof(类型,成员)”可以测试出成员地址和结构体起始地址之间的偏移量

    结构体内存对齐的原因

  • 平台原因(移植原因):不是所有的平台都能从任意地址访问任意数据,有一些平台只能从某些地址访问特定类型的数据,否则抛出硬件异常。
  • 性能原因:访问为对齐的内存处理器要进行两次内存访问,访问对齐的内存处理器只用仅从一次内存访问。
  • 总体来说:结构体的内存对齐使用空间来换取运行时间。
  • #include<stdio.h>//两个成员完全相同(顺序不同)的结构体,因为内存对齐造成结构体的内存大小不同
    struct Std1
    {
    char a;
    int b;
    char c;
    };
    struct Std2
    {
    char a;
    char b;
    int c;
    };
    int main()
    {
    printf("%zu\\n",sizeof(struct Std1));//12
    printf("%zu\\n", sizeof(struct Std2));//8
    return 0;
    }

    • 如果既要结构体内存对齐,又要节省内存空间,在定义结构体时把内存较小类型相同的成员放到一起并放到前面;
    • Windows系统下在结构体前面加上“#pragma pack()”可以改变编译器的默认对齐数(最好改为2的次方数)。

    结构体传参

    结构体传参分为两种方式:

  • 结构体变量传参;(会把结构体变量中的所有成员的值都传过去,可莪能会造成压栈现象,对时间和内存空间的消耗大)
  • 结构体指针传参。只用传输结构体的指针,最多8个字节,传输效率更高。
  • 结构体变量传参

    #include<stdio.h>
    struct Std1
    {
    char a;
    int arr[20];
    };
    void my_print(struct Std1 s)
    {
    s.a = '7';
    printf("%c\\n", s.a);
    }
    int main()
    {
    struct Std1 s1 = { 0 };
    my_print(s1);
    return 0;
    }

    结构体指针传参

    #include<stdio.h>
    struct Std1
    {
    char a;
    int arr[20];
    };
    void my_print(struct Std1* s)
    {
    s->a = '7';
    printf("%c\\n", s->a);
    }
    int main()
    {
    struct Std1 s1 = { 0 };
    my_print(&s1);
    return 0;
    }

    位段

    位段的声明

    位段的声明的和结构体的声明相似,只有两点不同:

  • 位段中的成员只能是整形家族(Int,signed int,unsigned int, char);
  • 位段中的成后面跟冒号和一个数字,数字表示为这个成员分配的比特位数。
  • struct Std
    {
    int a : 2;
    int b : 5;
    };

    这就是一个完整位段的声明。

    位段如何分配内存

  • 位段空间是按照4个字节(int),或者1个字节(char)进行开辟的;(开辟完空间后按照顺序从第一个成员开始存储,具体存储规则要看编译器)
  • 位段涉及到很多不确定因素,是不跨平台的,注重移植的程序不能使用位段。
  • 位段的不确定因素

  • 开辟之后的内存空间存储时是从左向右,还是从右向左存储;
  • 先开辟的内存空间不够下一个成员存储时,是否舍去剩余的几个比特位;
  • int位段是有符号数还是无符号数;
  • 位段中最大位的数目无法确定:16位的机器最大为16,32位的机器最大为32,16位的机器上给一个位段成员分配了27位就会出问题。
  • PS:

    • 与结构体相比,位段可以达到相同效果,可以节省内存空间,但是存在平台问题(应用于网络协议中)
    • 内存的最小单元为1个字节,但是在位段中一个字节中可以有几个成员存储,因此位段中的成员不能使用取地址符(&)取地址,也不能使用scanf()进行输入。

    联合体

    联合体像结构体一样,可由一个或多个成员构成,这些成员可以是不同类型,但是编译器只为占内存最大的成员分配足够的内存空间,所有的成员共用一个内存空间,所以联合体有另一个名字:共用体。

    联合体的声明

    除了关键字换为:union,其余声明方式与结构体一致。

    union Un
    {
    int a;
    char b;
    };

    由于联合体的所有成员共用一块内存,因此同一时间只能调用一个成员。

    联合体的大小

  • 至少为最大成员的内存大小;
  • 联合体整体大小必须是最大对齐数的整数倍。
  • union Un
    {
    char a[5];
    int b;
    };
    int main()
    {
    printf("%zu\\n", sizeof(union Un));//最大成员内存为5,但是最整个联合体的内存为:8
    return 0;
    }

    联合体的应用

    • 成员存在同一时间只用一个的情况;
    • 用于判断系统的大、小端字节序存储;
    • 和结构体嵌套使用。

    union Un
    {
    char a;
    int b;
    };
    int main()//VS环境下为:小端存储
    {
    union Un un;
    un.b = 1;
    if (un.a == 0)
    {
    printf("大端存储");
    }
    else
    {
    printf("小端存储");
    }
    return 0;
    }

    枚举

    把可能的值一一列举。

    枚举的声明

    enum SEX
    {
    MALE,
    FEMALE,
    SECRET
    };

    列举的每种情况之间用逗号隔开,若没有特殊赋值,系统会默认给第一个赋值“0”,后续的依次加一,枚举的每种情况都有对应的值。(声明时可以给每一种情况赋特殊值)

    枚举的使用

    enum SEX sex = MALE;//把其中一种情况赋值

    枚举相比于宏定义“#define”的优点

  • 增加代码的可读性和可维护性;
  • 枚举有类型检查,更加严谨;
  • 便于调试,预处理阶段会删除“#define”定义的符号;
  • 使用方便,依次可以定义多个常量;
  • 枚举常量遵循作用域规则,若在函数内声明,则只能在函数内使用。
  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » C语言-自定义类型结构体
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!