14 为什么联合体大小不是成员总和?C 语言联合体计算原理 + 枚举使用 + 注意事项梳理

发布于:2025-09-04 ⋅ 阅读:(15) ⋅ 点赞:(0)

 联合体和枚举类型

1 联合体类型的声明

像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。给联合体其中⼀个成员赋值,其他成员的值也跟着变化。

#include <stdio.h>
//联合类型的声明
union Un
{
	char c;
	int i;
};
int main()
{
//联合变量的定义
	union Un un = {0};
//计算连个变量的⼤⼩
	printf("%d\n", sizeof(un));//4--int类型的大小
	return 0;
}

联合体的特点

联合的成员是共⽤同⼀块内存空间的,这样⼀个联合变量的⼤⼩,⾄少是最⼤成员的⼤⼩(因为联合 ⾄少得有能⼒保存最⼤的那个成员)。同一时间只能有效存储一个成员的值,所以只能访问存储值的成员。

//代码1
#include <stdio.h>
//联合类型的声明
union Un
{
	char c;
	int i;
};
int main()
{
//联合变量的定义
	union Un un = {0};
// 每个成员的地址都是一样的,说明每个成员共用一块内存,内存大小一般是成员中数据类型最大的哪个
	printf("%p\n", &(un.i));//001AF85C
	printf("%p\n", &(un.c));//001AF85C
	printf("%p\n", &un);//001AF85C
	return 0;
}
//代码2
#include <stdio.h>
//联合类型的声明
union Un
{
	char c;
	int i;
};
int main()
{
//联合变量的定义
	union Un un = {0};
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);//11223355
	return 0;
}

c语言中使用联合体的注意点

  1. 内存共享特性:

  • 联合体所有成员共享同一块内存空间

  • 大小由最大成员决定

  • 同一时间只能有效存储一个成员的值

2.类型转换问题:

  • 访问非当前存储的成员会导致未定义行为

  • 需要确保访问的成员与最后存储的成员一致

3.初始化限制:

  • 只能初始化第一个成员

  • 其他成员需要通过赋值操作单独设置

4.字节序问题:

  • 在不同字节序的机器上可能有不同表现

  • 常用于检测系统字节序

初始化规则:

  1. 默认初始化限制‌ 默认只能初始化‌第一个成员‌(C89/C90标准):

union Data {
    int i;
    float f;
    char str[20];
};
union Data d1 = {10}; // 正确:初始化第一个成员i=10

关键注意事项:

  • 内存覆盖‌:修改任一成员会覆盖其他成员的值(共享同一内存)

  • 初始化有效性‌:只能保证被初始化的成员值是有效的

  • 未初始化访问‌:读取未初始化的成员导致‌未定义行为‌(可能崩溃或垃圾值)

  • 结构体嵌套‌:当联合体嵌套在结构体中时,可通过结构体初始化器初始化:

union Un
{
    int i;
    char c;
};
#include<stdio.h>
int main()
{
    Un un = { 0 };
    un.i = 0x11223344;
    printf("%x\n", un.i);
    printf("%x\n", un.c);

    un.c = 0x55;
    printf("%x\n", un.i);
    printf("%x\n", un.c);
}
//11223344
//44
//11223355 给成员c赋值覆盖了成员i的一个字节的数据
//55

最佳实践

要使用联合体的哪个成员就给哪个成员赋值,使用完后才给其它成员赋值,因为给其它成员赋值会覆盖掉原来成员的值因为是共用一块内存。

联合体⼤⼩的计算

• 联合的⼤⼩⾄少是最⼤成员的⼤⼩。

• 当最⼤成员⼤⼩不是最⼤对⻬数(编译器的默认对齐数)的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。

#include <stdio.h>
union Un1
{
	char c[5];
	int i;
};
union Un2
{
	short c[7];
	int i;
};
int main()
{
	//下⾯输出的结果是什么?
	printf("%d\n", sizeof(union Un1));//8---最大成员大小5字节,默认对齐数8字节
	printf("%d\n", sizeof(union Un2));//16--最大成员字节数14,默认对齐数8
	return 0;
}

2. 枚举类型

枚举类型的声明

枚举顾名思义就是⼀⼀列举。把可能的取值⼀⼀列举。⽐如我们现实⽣活中:⼀周的星期⼀到星期⽇是有限的7天,可以⼀⼀列举性别有:男、⼥、保密,也可以⼀⼀列举⽉份有12个⽉,也可以⼀⼀列举三原⾊,也是可以意义列举这些数据的表⽰就可以使⽤枚举了。可以看作宏定义的打包。

enum Day//星期
{
	Mon,//看作#define Mon 0;
	Tues,//看作#define Tues 1;
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};
enum Sex//性别
{
	MALE,
	FEMALE,
	SECRET
};
enum Color//颜⾊
{
	RED,
	GREEN,
	BLUE
};

以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。 { }中的内容是枚举类型的可能取值,也叫 枚举常量 。 这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。

enum Color//颜⾊
{
	RED=2,
	GREEN=4,
	BLUE=8
};

枚举类型的优点

为什么使⽤枚举? 我们可以使⽤ #define 定义常量,为什么⾮要使⽤枚举 枚举的优点:

  1. 增加代码的可读性和可维护性--枚举变量只能使用定义的枚举值赋值或者初始化

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

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

  4. 使⽤⽅便,⼀次可以定义多个常量

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

枚举类型的使⽤

enum Color//颜⾊
{
RED=1,
GREEN=2,
BLUE=4
};
enum Color clr = GREEN; //使⽤枚举常量给枚举变量赋值或者整形,枚举变量底层就是整形变量

那是否可以拿整数给枚举变量赋值呢?在C语⾔中是可以的,但是在C++是不⾏的,C++的类型检查⽐ 较严格。

最佳实践

枚举类型中枚举了哪些枚举常量就使用哪些常量不要使用整数赋值给枚举变量;

读到这里已经超越百分之90的人咯,每天坚持进步一点点,只和过去的自己比。