C语言自定义类型——【结构体,枚举,联合】(二)

目录

二、位段

2.1 什么是位段

2.2 位段的内存分配

2.3 位段的跨平台问题

三、枚举——enum

3.1 枚举的定义类型

3.2 枚举的优点

四、联合(共用体)——union

4.1 联合的定义

4.2 使用实例

4.3 联合体大小


二、位段

(书接上文结构体,现在讲位段,跟结构体有些类似~)

2.1 什么是位段

位段与结构体类似,但也有不同:

1. 位段成员必须是int、unsigned int、signed int(char 也可以)。

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

举个栗子~

struct A
{
    int _a : 2;
    int _b : 5;
    int _c : 10;
    int _d : 30;
};

不同于结构体,位段多了冒号和数字。那么这冒号和数字有什么含义呢?其实,冒号后的数字代表了比特位。比如上面的例子中,_a这个成员只占2个比特位,_b这个成员只占5个比特位。如果这样写的话,sizeof(A)就只有8个字节。

但注意,这种写法需要根据具体特殊情况使用。因为正常的int的范围很大,而如果限定为2个字节如上面的_a,则范围只能在0~3,如果你现在有一个成员你只需要它表示0~3则可以用这种位段来表示。因此,位段可以在某些特殊情况下给我们节约空间。


2.2 位段的内存分配

  1. 位段成员:int / unsigned int / signed int / char 
  2. 位段空间是按要求需要以4字节或者1字节的方式开辟。
  3. 位段不跨平台,注重可移植性的程序应避免使用位段。

一个栗子,我们给出的A究竟占几个字节呢?

struct A
{
    int _a : 2;
    int _b : 5;
    int _c : 10;
    int _d : 30;
};
  • 首先一看是int,那我们先分配四个字节。4个字节一共有32个比特位,然而我这个_a只占用了2个比特位,用完后就剩30个比特位。接着_b占用了5个比特位,用完后就剩25个比特位。接着_c占用了10个比特位,用完后还剩15个比特位。
  • 然后_d需要30个比特位,此时不够了。于是我们又在内存中申请一个int的大小,4个字节。那么_d会不会用之前剩下的15个比特位呢?还是直接用新开辟的?这个问题是没有标准的,这个就造成了位段是不跨平台的。因为不同的平台处理这个问题的选择是不同的。
  • 但无论如何,这个例子中的A的大小都是8个字节。

另一个栗子:

struct S
{
    char a : 3;
    char b : 4;
    char c : 5;
    char d : 4;
};
struct S s = {0};
s.a = 10;
s.c = 12;
s.c = 3;
s.d = 4;
  • 因为是char,所以我们先开辟1个字节(00000000)。首先看a,a只占了3个比特位,那么这三个是占前三个还是后三个呢?没有规定。所以说位段不好跨平台使用。那么我们假设a占的是后三个比特位吧!然后是b,b占了4个比特位,现在后7个比特位就都被占用了。
  • 然后还剩1个比特位了。c需要5个,不够。那么就再开辟1个字节也就是8个比特位。那么我们假设我们把这1个浪费了即不用剩下的这1个比特位,让c占用新开辟的1个字节中的后5个比特位。还剩3个比特位。
  • 最后为了d再开辟一个字节,最后四个比特位给d。如果这样的话s的大小就是三个字节。

我们可以通过内存的查看来验证我们的猜想。a放的三个比特位是10的二进制序列1010的后三个010,b放的四个是1100,c放的是00011,d放的是0100。然后按照上面的猜想将他们放在三个字节中:01100010 00000011 00000100。我们在调试窗口中的内存可以查看具体内存的存储内容来验证。


2.3 位段的跨平台问题

很多问题:int有无符号、位段的最大数目、从左向右还是从右向左分配、是否舍弃剩余位置。

只能根据不同编译器写不同的代码。


三、枚举——enum

顾名思义:一一列举。

3.1 枚举的定义类型

比如,性别分成男、女、保密,所以性别就可以定义成枚举类型:

enum Sex
{
    MALE,
    FEMALE,
    SECRET
};

我们用enum定义一个名为Sex的枚举类型,未来可能的取值有MALE FEMALE SECRET。然后用的时候我们就可以定义enum Sex s = MALE;这样一个变量。

再举个栗子:

一个星期也可以定义成枚举类型,一共有7中可能取值。

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

(我们枚举的一定是容易、适合枚举的东西~)

另外,枚举的可能取值都是有值的,可以直接printf("%d", Mon);这样打印出来。第一个值默认为零,后面的依次递增1。当然,我们也可以自定义初值,比如在定义时让Mon = 5,那么Tues的值就是6。


3.2 枚举的优点

我们将枚举常量与#define定义的常量对比有很多优点,比如#define Mon 5与在枚举中定义Mon = 5,其实枚举常量中的更具有可读性,更有意义;并且枚举有类型检查;还可以防止命名污染;并且便于调试(调试的都是预编译之后的代码,然而用define的话,预编译之后的代码已经将定义的常量直接替换成了数值,不便于调试);更方便使用,一次定义多个常量

建议大家在定义常量的时候多考虑枚举~


四、联合(共用体)——union

4.1 联合的定义

这种类型的变量包含一系列的成员,这些成员共用同一快空间

栗子:

union Un
{
    char c;
    int i;
};

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

如果你认为,char是1,加上int是4,答案应该是5,考虑对齐应该是8。那么你就是把他错当成结构体了。事实上,结果应该是4。

为了验证,我们可以printf("%p\n", &u);printf("%p\n", &(u.c));printf("%p\n", &(u.i));,会发现这三个结果相同,说明两个成员共用内存。所以我们也称之为共用体。改c的时候i也变了,改i的时候c也变了。所以联合体的成员不会同时使用。


4.2 使用实例

判断机器存储模式(大小端):

//原来的方法
int main()
{
    int i = 1;
    //01 00 00 00
    if(1 == *(char*)&i)
    {
        printf("小端\n");
    }
    else
    {
        printf("大端\n");
    }
    return 0;
}


//用联合的方法

int check_sys()
{
    union Un
    {
        char c;
        int i;
    }u;
    u.i = 1;
    return u.c;
}

int main()
{
    if(1 == check_sys())
    {
        printf("小端\n");
    }
    else
    {
        printf("大端\n");
    }
    return 0;
}
  

4.3 联合体大小

栗子:

union Un
{
    char arr[5];
    int i;
};

我们来计算它的大小,看起来应该是5。但结果是8。因为存在对齐。char类型的数组对齐数为1。然而int的对齐数是4,最大对齐数是4,所以5不是最大大小,应该变成8。

所以联合体的大小不一定是最大成员的大小,至少是最大成员的大小。如果最大成员的大小不是最大对齐数的整数倍,需要对齐。


好啦!C语言自定义类型——【结构体,枚举,联合】的介绍就结束啦!接下来的博客会有个具体实例使用的介绍!感谢大家的阅读!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值