自定义类型:结构体,枚举和联合体详讲

这里写目录标题

  • 结构体
    • 基础知识
    • 结构体的内存对齐
    • 结构体传参
  • 位段
    • 基础知识
    • 内存分配
  • 枚举
    • 基础知识
  • 联合体
    • 基础知识
    • 联合体大小计算

结构体

基础知识

结构体的声明

struct stu//创建一个名字为stu的结构体
{
     
    char name[20];//下面三个为成员变量
    int age;
    short price;
};//注意:此“;”必须加

结构体的定义和初始化

#include
struct stu//创建一个名字为stu的结构体
{
     
    char name[20];//下面三个为成员变量
    int age;
    short price;
}s1;//注意:此“;”必须加
//定义了一个名称为s1的结构体,但是没有赋值
int main( )
{
     
    struct stu s2 = {
     "zhangsan",18,90};//定义一个s2的结构体,并为其赋值
}

结构体的使用

#include
struct stu//创建一个名字为stu的结构体
{
     
    char name[20];//下面三个为成员变量
    int age;
    short price;
}s1;//注意:此“;”必须加
//定义了一个名称为s1的结构体,但是没有赋值
int main( )
{
     
    struct stu s2 = {
     "zhangsan",18,90};//定义一个s2的结构体,并为其赋值
    struct stu* ps = &s2;//定义一个指向s2的指针
    printf("%s %d %d\n",s2.name,s2.age,s2.price);//直接用结构体名称+.代表结构体中的元素
    printf("%s %d %d",ps->name,ps->age,ps->price);//如果使用指针,就要使用"->"
}

输出

zhangsan 18 90
zhangsan 18 90

结构体的内存对齐

结构体再计算大小时,会发生对齐
offsetof函数
计算结构体成员相对于结构体起始位置的偏移量
用法:size_t offsetof(structName,memberName);
此函数可以计算struct的某个成员的偏移量
偏移量:把存储单元的实际地址与其所在段的段地址之间的距离称为段内偏移,也称为"有效地址或偏移量。即为该成员变量的地址与初始位置的字节数

#include
#include
struct S
{
     
    char c1;
    int i;
    char c2;
};
int main( )
{
     
    printf("%d\n",offsetof(struct S,c1));
    printf("%d\n",offsetof(struct S,i));
    printf("%d\n",offsetof(struct S,c2));
}
0
4
8

结构体的对齐规则
1.第一个成员在与结构体偏移量为0的位置,所以上式C1的偏移量为0
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
对齐数 = 编译器默认的对齐数与该成员大小的较小值
VS默认对齐数为8
则上面的i是int类型,较小值为4字节,因此偏移量为4
c2为char类型,对齐数为1,偏移量为8(上面int占用4个字节)
3.结构体总体大小为最大对齐数(每一个成员变量都有一个对齐数)的整数倍,所以上述结构体的大小为12(4的倍数),因为9不是4的倍数。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

#include

struct S1
{
     
    char c1;//偏移量 0
    char c2;//偏移量 1
    int i;//偏移量   4
};

struct S2
{
     
    int i;//偏移量 0
    double d;//偏移量 8
    struct S1 s1;//偏移量 16
};

int main( )
{
     
    printf("%d\n",sizeof(struct S1));//8
    printf("%d",sizeof(struct S2));
    return 0;
}
8
24

因为24为最大对齐数8的倍数,因此就为24

修改默认对齐数

#include
#pragma pack(4)//修改默认对齐数为4
struct S1
{
     
    char c1;//偏移量 0
    char c2;//偏移量 1
    int i;//偏移量   4
};

struct S2
{
     
    int i;//偏移量 0
    double d;//偏移量 4
    struct S1 s1;//偏移量 12
};

#pragma pack()//取消设置的默认对齐数

int main( )
{
     
    printf("%d\n",sizeof(struct S1));//8
    printf("%d",sizeof(struct S2));//20
    return 0;
}
8
20

使用 pragam pack修改默认对齐数,可以节省内存空间
另外,在设计结构体时,让占用空间小的成员尽量集中在一起可以节省空间

结构体传参

void test1(struct S1 s1);
void test2(struct S1* ps);

int main( )
{
     
    struct S1 s1;
    test1(s1);//传整个结构体
    test2(&s1);//传结构体地址,推荐使用传址的方式

使用传地址的方式,可以节省空间,尽量使用该种方式

位段

基础知识

位段的声明,定义和初始化
位段的声明与结构体基本相同,但有如下区别:
1.位段的成员一般为int,unsigned int, signed int;
2.位段的成员名后有一个冒号和一个数字,数字表示该变量需要的比特位

#include

struct A//创建一个A的位段
{
     
    int _a : 2;//_a只需要2个比特位,数字表示比特位
    int _b : 5;
    int _c : 10;
    int _d : 30;
};

位段的定义以及初始化与结构体类似

内存分配

位段的跨平台问题
1.int位段被当成是有符号数还是无符号数是不确定的
2.位段中最大位的数目是不确定的(64位机器最大16,32位机器最大32)
3.位段中的成员是从右到左分配还是从左向右分配是不确定的
4.当一个结构包含两个位段,第一个位段成员比较大,无法容纳于第一个位段剩余的比特位时,是舍弃剩余的位还是利用是不确定的
举例说明

#include

struct A//创建一个A的位段
{
     
    int _a : 2;//_a只需要2个比特位,数字表示比特位
    int _b : 5;
    int _c : 10;
    int _d : 30;
};

int main()
{
     
    printf("%d",sizeof(struct A));//由于需要存放_d,之前开辟的4个字节的空间(32bit)不足,则新开辟一个int(4字节)
    return 0;
}
8
#include
//设当前平台上,前一个空间剩余的比特位不足时直接浪费,一个字节从低位置到高位置使用
struct A
{
     
    int _a : 3;
    int _b : 4;
    int _c : 5;
    int _d : 4;
};

int main()
{
     
    struct A a;
    a._a = 10;
    a._b = 12;
    a._c = 3;
    a._d = 4;
    return 0;
}

内存分配图示:
自定义类型:结构体,枚举和联合体详讲_第1张图片
但注意:该位段的内存大小为4

枚举

基础知识

定义

enum RGB
{
     
    RED,//注意这里为逗号,不是分号
    GREEN,
    BLUE,
};

这些成员都是有值,如果没有赋值,默认从0开始,依次递加1,在定义时可以赋初值

enum RGB
{
     
    RED = 1,//注意这里为逗号,不是分号
    GREEN,
    BLUE,
};

只能拿枚举常量给枚举变量赋值,才不会出现类型的差异

#include
enum RGB
{
     
    RED = 1,//注意这里为逗号,不是分号
    GREEN,
    BLUE,
};

int main( )
{
     
    enum RGB cle = GREEN;
    printf("%d",cle);
    return 0;
}
2

联合体

基础知识

联合体的声明
联合体又称共用体,即成员共有同一块内存空间

#include
union Un
{
     
    char c;
    int i;
};

int main( )
{
     
    union Un u;
    printf("%d",sizeof(u));
    return 0;
}
4

联合体大小计算

1.联合体的大小至少为最大成员的大小
2.当最大成员大小不是最大对齐数的整数倍时,要对齐到最大对齐数的整数倍

#include
union Un
{
     
    short arr[7];//对齐数:2,整个大小为14,不是4的倍数,因此内存大小为16
    int i;//对齐数:4
};

int main( )
{
     
    union Un u;
    printf("%d",sizeof(u));
    return 0;
}
16

利用联合体判断电脑是大端还是小端存储
思路:利用联合体共用内存空间的特点,将1存进int类型中,看char的值为1还是0
实现

#include
union Un
{
     
    char c;
    int i;
}u;

int main( )
{
     
    u.i = 1;
    if(u.c == 0)
    {
     
        //大端存储即为数据低位存储到地址高位置
        printf("大端存储\n");// 存储为 00 00 00 01
    }
    if(u.c == 1)
    {
     
        //小端存储为数据低位存储到地址低位置
        printf("小端存储\n");//存储为 01 00 00 00
    }
    return 0;
}
小端存储

你可能感兴趣的:(自定义类型:结构体,枚举和联合体详讲)