C语言进阶:共用体和枚举
一、共用体
共用体看起来很像结构体,只不过关键字由 struct 换成了 union 。由于 union 单词本意是联合的意思,因此也叫联合体。它与结构体的主要区别在于:
- 结构体定义一个由多个数据成员组成的特殊类型
- 共用体定义了一块为所有数据成员共享的内存
1.1、共用体(union)的概念
共用体使得几种不同类型的变量存放到同一个内存单元之中,因此,在某一个时刻共用体有且只有一个值——某个数据成员的值。由于所有成员共享一块内存,因此共用体的大小等于最大成员的大小。
定义共用体的一般形式如下:
union 共用体名称 {成员列表
} 变量列表;例如:
union MyFirstUnion {int a;char b;float c;
} myFirstUnion;上述定义了一个共用体类型 union MyFirstUnion(可以使用它来声明共用体变量),其中 myFirstUnion 为定义共用体的变量。它占用32个比特位的空间,因为float类型占用空间最大为32位,所以整个共用体的大小就是32位。
可以看到,共用体和结构体不管是在定义上还是变量的声明上都是非常相似的。不过,结构体变量的大小是所有数据成员大小的总和,每个成员都有自己的内存单元,而且还遵循结构体内存对齐等一系列规则;然而,共用体的大小则是数据成员中最大内存长度的大小。例如:上述共用体大小就是32bit与float类型大小一致。
1.2、共用体变量的引用
共用体变量定义完成后,就可以引用其中的数据成员。引用形式为:“ 共用体变量名称 . 成员名称”。以前面定义的 myFirstUnion 为例:
myFirstUnion.a;
myFirstUnion.b;
myFirstUnion.c;注意:不能在输出语句中直接引用共用体实体变量,如:printf("%d", myFirstUnion)。因为这是一种不明确的引用,编译器不知道是哪一个成员,可能会导致程序崩溃或是得到一个意料之外的值。
简单实例:设计一个交通工具的共用体,让用户自行选择出行方式。
思考:在某一特定时间内,人们采取的出行方式只能有一种。换言之,你选择了坐公交车,就不可能在公交车上开私家车。
实例代码如下:
#include <stdio.h>
#include <string.h>
// 坐公交
typedef struct bus {char name[50];
} Bus;// 开小车
typedef struct car {char name[50];
} Car;// 出行方式
union Transport{Bus a;Car b;
};int main() {union Transport t;strcpy(t.a.name, "公交车");strcpy(t.b.name, "五菱宏光");printf("出行方式:%s\n", t.a.name);printf("出行方式:%s\n", t.b.name);return 0;
}在程序中,在同一时间内改变共用体的某一个成员,其他成员也会随之改变。当给某个特定成员赋值时,其他成员的值也会具有相同的含义,这是因为他们的值的每一个比特位都被新的值所覆盖。这正好表明共用体中的变量使用的空间是同一片空间,只要有一个变了,其他的也随之变化。执行效果如下图所示:

1.3、共用体变量的初始化
定义共用体变量时,可以同时进行初始化操作。与结构体类似,初始化的值要放在大括号中。需要注意的是:
对共用体变量进行初始化时,只需要给一个初始化的值就够了,其类型必须和共用体的第一个成员的类型一致。如果共用体的第一个成员是结构体类型,则初始化的值可以包含多个用于初始化该结构体的表达式。例如:
#include <stdio.h>
union Test {char a;int b;
}int main(){union Test test = {128};printf("%d\n", test.b);return 0;
}输出结果图:

显然,输出与实际值不相同,这是因为,初始化时 128 是赋值在变量 a 上面的,而 a
是一个有符号的 char,取值在 -128~127 之间,128超出了他的取值范围。使用%d打印应该输出 -128才对,然而容易被忽视的是 char 只占用 1 个字节,而 b 是 int 类型,占用 4 个字节,剩下的三个字节被编译器随机分配了。且看下面几幅图(gcc 小端序):



可见,每次结果不尽相同。说明得到的是一个随机值,而 test.a 与分析一致,输出 -128。
1.4、共用体数据特点总结
在使用共用体时,应注意以下几点:
- 同一内存虽然可以用来存放不同类型成员,但是每次只能存放一种类型,而不能同时存放所有类型。也就是说,无论共用体中有多少变量,在同一时间类,起作用的有且只有一个。
- 共用体变量中,起作用的永远是最后存放的成员。存入新成员后,原有成员将失去作用。
- 共用体变量的地址与成员地址是一样的,因为他们指向同一片空间
- 不要直接对共用体变量名赋值(看上述示例),也不能企图引用变量名来得到一个值
正确赋值方式,明确指明初始化变量:

二、枚举
实际问题中,有些变量的值会被限定在一个范围内。比如,一周只有7天,一年只有12个月等等。如果把这些变量声明为整型、字符型等显得有些不够妥当。为此:C 语言提供了枚举类型。
枚举类型是一种基本数据类型,不能被二次拆分。利用关键字 enum 进行声明,并使用枚举类型来定义变量。一个枚举类型包含一组相关的标识符,每个标识符对应一个整数值,成为枚举常量。
例如(定义三原色):
enum Colors(Red,Green,Blue);Colors是枚举类型变量,括号中第一个标识符对应的值是0,其后依次递增1。
注意:每个标识符都是唯一的,且不能采用关键字或当前作用域以内其他相同的标识符名称。
在定义枚举变量时,可以指定初始值,后面的值依次加1,例如:
enum Colors(Red = 1,Green,Blue);这样 Red = 1, Green = 2, Blue = 3。
示例代码:
#include <stdio.h>
enum Color {Red = 1,Blue,Green
} color;int main () {int chooseColor = 0;printf("1表示红色,2表示蓝色,3表示绿色\n,请选择你心仪的颜色(输入对应的数字即可):");scanf("%d", &chooseColor);switch(chooseColor) {case Red:printf("\n红色\n");break;case Blue:printf("\n蓝色\n");break;case Green:printf("\n绿色\n");break;default:printf("\n没有这种颜色\n");break;}return 0;
}执行效果图如下:

三、typedef关键字详解
typedef 的作用是为某个数据类型定义一个新的名字。这里的数据类型可以是基本数据类型(int、char、float等等)、数组类型、指针类型。也可以是用户自定义的数据类型:结构体、共用体、枚举等等。
使用 typedef 关键字的目的一般有两个:一是给复杂类型进行简化(复杂函数指针类型),二是给变量取一个容易记住的名字。
3.1、为基本数据类型定义别名
例如:
typedef long long Llong;这里Llong就代表long long 这种数据类型,可以用来定义长整型变量。
3.2、为结构体、共用体和枚举类型定义简洁的名称
例如:
typedef struct Node {int data;struct Node * next;
} N;这里的N,就表示结构体类型struct Node。
3.3、为数组类型定义简介的名称
例如:
typedef int Int_Array[50];
Int_Array[50] arr;这里的arr就表示Int_Array[50]。
3.4、为指针类型定义别名
例如:
typedef float* ptrF;这样,就要可以用ptrF来表示一个指向float类型数据的指针
