1.联合体

1.1 联合体类型的声明

像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。

但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同⼀块内存空间。所以联合体也叫:共用体。

给联合体其中⼀个成员赋值,其他成员的值也跟着变化。

1.2联合体的特点

  • 联合的成员是共用同⼀块内存空间的,这样⼀个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

  • 我们看如下代码,&un 和 &un.i 和 &un.c 的地址是同一个地址,我们可以得出char 和int 公用一个内存空间 4

union Un {
    char c;
    int i;
};
​
int main() {
    union Un un = {0};
    printf("%zd", sizeof(un));
    printf("%zd", &un);
    printf("%zd", &un.c);
    printf("%zd", &un.i);
}

1.3联合体大小计算

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

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

我们看如下代码 最大成员大小为 5 不是4的倍数 那就浪费三个字节 最后占8个字节

union Un {
​
    char c[5];// 等于5个char类型 对齐数是1 8(最大) 1
    int i;//3                         4 8      4
    //然后 5不是4的倍数 那就浪费三个字节 最后占8个字节
​
};
int main() {
    printf("%zd", sizeof(union Un));
}

1.4什么场景使用联合体?

使用联合体是可以节省空间的,举例:

比如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。

每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。

图书:书名、作者、页数

杯⼦:设计

衬衫:设计、可选颜色、可选尺存

那我们不思考,直接写出⼀下结构:

struct gift_list
{
    //公共属性
    int stock_number;//库存量
    double price; //定价
    int item_type;//商品类型
​
    char title[20];//书名
    char author[20];//作者
    int num_pages;//⻚数
    char design[30];//设计
    int colors;//颜⾊
    int sizes;//尺⼨
};

上述的结构其实设计的很简单,⽤起来也⽅便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的大小就会偏大,比较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常用的。比如:商品是图书,就不需要design、colors、sizes。

所以我们就可以把公共属性单独写出来,剩余属于各种商品本⾝的属性使用联合体起来,这样就可以介绍所需的内存空间,⼀定程度上节省了内存。

  • 用联合体类型的话,他们私有的属性就独立出来了,内存总共就给他们一个空间,然后他们共用一份空间,谁用的时候就占用这一份空间(使用的时候才创建),那就在一定程度上节省了空间

  • 结构体用匿名类型

  • 访问的时候 先找到gift_list结构体再找item 最后找你想要的

struct gift_list
{
 int stock_number;//库存量
 double price; //定价
 int item_type;//商品类型
 
 union {
 struct
 {
 char title[20];//书名
 char author[20];//作者
 int num_pages;//⻚数
 }book;
 struct
 {
 char design[30];//设计
 }mug;
 struct
 {
 char design[30];//设计
 int colors;//颜⾊
 int sizes;//尺⼨
 }shirt;
 }item;
};
  • 接下来我们回忆一下前面判断机器大小端的练习,现在我们用联合体去判断


union  Un
{
    char c;
    int i;
};
​
int main() {
    union Un u = { 0 }; //初始化
    u.i = 1;
    if (u.c == 1) {
        printf("小端");
    }
    else
    {
        printf("大端");
    }
}

2.枚举类型

2.1枚举类型声明

枚举顾名思义就是⼀⼀列举。

把可能的取值⼀⼀列举。

比如我们现实生活中:

⼀周的星期⼀到星期日是有限的7天,可以⼀⼀列举

性别有:男、女、保密,也可以⼀⼀列举 等等

这些数据的表示就可以使用枚举了。

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

这些可能取值都是有值的,默认从0开始,依次递增1

//枚举类型
enum Sex {
    MALE,
    FEMALE,
    SECRET
​
};
​
int main() {
    enum Sex sex1 = MALE;
    printf("%d\n", MALE);
    printf("%d\n", FEMALE);
    printf("%d\n", SECRET);
}
当然在声明枚举类型的时候也可以赋初值,第一个常量永远是0其他的一次递增

enum Sex {
    MALE,
    FEMALE=2,
    SECRET=
​
};
​
int main() {
    enum Sex sex1 = MALE;
    printf("%d\n", MALE);
    printf("%d\n", FEMALE);
    printf("%d\n", SECRET);
}
  • 我们会疑惑,为什么常量可以修改呢? 这并不是修改,只是给一个初始值,我们可以看到以下的赋值语句 MALE = 5 报错

//枚举类型
enum Sex {
    MALE,
    FEMALE=2,
    SECRET=3
​
};
​
int main() {
    enum Sex sex1 = MALE;
    MALE = 5; //这里不能赋值成功 修改失败
    printf("%d\n", MALE);
    printf("%d\n", FEMALE);
    printf("%d\n", SECRET);
}

2.2 枚举类型的优点

为什么使用枚举?

我们可以使用 #define 定义常量,为什么非要使用枚举?

枚举的优点:

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

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

  3. 便于调试,预处理阶段会删除 #define 定义的符号

  4. 使用方便,⼀次可以定义多个常量

  5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用

下面举一个加法器的例子体现枚举类型的优点,定义一个枚举类型可以让代码更有可读性

//定义一个枚举类型 提高代码的可读性
enum Option {
    EXIT = 0,
    ADD,
    SUB,
    MUL,
    DIV
};
​
void menu() {
    printf("***********************************");
    printf("********1.add   2.sub**************");
    printf("********3.mul   4.div**************");
    printf("********    0.exit   **************");
}
int main() {
    int input = 0;
    do {
        menu();
        printf("请选择你想要的计算:");
        
        scanf("%d", &input);
        switch (input)
        {
        case ADD:  //方法名 比 1, 2 ,3 更有可读性,便于理解
            Add();
            break;
        case SUB:
            Sub();
            break;
        case MUL:
            Mul();
            break;
        case DIV:
            Div();
            break;
        case EXIT:
            break;
        default:
            printf("选择有误,请重新选择");
        }
    } while (input);
​
}

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部