Union 结构
有时需要一种数据结构,不同的场合表示不同的数据类型。比如,如果只用一种数据结构表示水果的“量”,这种结构就需要有时是整数(6个苹果),有时是浮点数(1.5公斤草莓)。
C 语言提供了 Union 结构,用来自定义可以灵活变更的数据结构。它内部可以包含各种属性,但同一时间只能有一个属性,因为所有属性都保存在同一个内存地址,后面写入的属性会覆盖前面的属性。这样做的最大好处是节省空间。
union quantity {
short count;
float weight;
float volume;
};
上面示例中,union
命令定义了一个包含三个属性的数据类型quantity
。虽然包含三个属性,但是同一时间只能取到一个属性。最后赋值的属性,就是可以取到值的那个属性。
使用时,声明一个该类型的变量。
// 写法一
union quantity q;
q.count = 4;
// 写法二
union quantity q = {.count=4};
// 写法三
union quantity q = {4};
上面代码展示了为 Union 结构赋值的三种写法。最后一种写法不指定属性名,就会赋值给第一个属性。
执行完上面的代码以后,q.count
可以取到值,另外两个属性取不到值。
printf("count is %i\n", q.count); // count is 4
printf("weight is %f\n", q.weight); // 未定义行为
如果要让q.weight
属性可以取到值,就要先为它赋值。
q.weight = 0.5;
printf("weight is %f\n", q.weight); // weight is 0.5
一旦为其他属性赋值,原先可以取到值的q.count
属性就不再有效了。除了这一点,Union 结构的其他用法与 Struct 结构,基本上是一致的。
Union 结构也支持指针运算符->
。
union quantity {
short count;
float weight;
float volume;
};
union quantity q;
q.count = 4;
union quantity* ptr;
ptr = &q;
printf("%d\n", ptr->count); // 4
上面示例中,ptr
是q
的指针,那么ptr->count
等同于q.count
。
Union 结构指针与它的属性有关,当前哪个属性能够取到值,它的指针就是对应的数据类型。
union foo {
int a;
float b;
} x;
int* foo_int_p = (int *)&x;
float* foo_float_p = (float *)&x;
x.a = 12;
printf("%d\n", x.a); // 12
printf("%d\n", *foo_int_p); // 12
x.b = 3.141592;
printf("%f\n", x.b); // 3.141592
printf("%f\n", *foo_float_p); // 3.141592
上面示例中,&x
是 foo 结构的指针,它的数据类型完全由当前赋值的属性决定。
typedef 命令可以为 Union 数据类型起别名。
typedef union {
short count;
float weight;
float volume;
} quantity;
上面示例中,union
命令定义了一个包含三个属性的数据类型,typedef
命令为它起别名为quantity
。
Union 结构的好处,主要是节省空间。它将一段内存空间,重用于不同类型的数据。定义了三个属性,但同一时间只用到一个,使用 Union 结构就可以节省另外两个属性的空间。Union 结构占用的内存长度,等于它内部最长属性的长度。
Enum 类型
如果一种数据类型的取值只有少数几种可能,并且每种取值都有自己的含义,为了提高代码的可读性,可以将它们定义为 Enum 类型,中文名为枚举。
enum colors {RED, GREEN, BLUE};
printf("%d\n", RED); // 0
printf("%d\n", GREEN); // 1
printf("%d\n", BLUE); // 2
上面示例中,假定程序里面需要三种颜色,就可以使用enum
命令,把这三种颜色定义成一种枚举类型colors
,它只有三种取值可能RED
、GREEN
、BLUE
。这时,这三个名字自动成为整数常量,编译器默认将它们的值设为数字0
、1
、2
。相比之下,RED
要比0
的可读性好了许多。
注意,Enum 内部的常量名,遵守标识符的命名规范,但是通常都使用大写。
使用时,可以将变量声明为 Enum 类型。
enum colors color;
上面代码将变量color
声明为enum colors
类型。这个变量的值就是常量RED
、GREEN
、BLUE
之中的一个。
color = BLUE;
printf("%i\n", color); // 2
上面代码将变量color
的值设为BLUE
,这里BLUE
就是一个常量,值等于2
。
typedef 命令可以为 Enum 类型起别名。
typedef enum {
SHEEP,
WHEAT,
WOOD,
BRICK,
ORE
} RESOURCE;
RESOURCE r;
上面示例中,RESOURCE
是 Enum 类型的别名。声明变量时,使用这个别名即可。
还有一种不常见的写法,就是声明 Enum 类型时,在同一行里面为变量赋值。
enum {
SHEEP,
WHEAT,
WOOD,
BRICK,
ORE
} r = BRICK, s = WOOD;
上面示例中,r
的值是3
,s
的值是2
。
由于 Enum 的属性会自动声明为常量,所以有时候使用 Enum 的目的,不是为了自定义一种数据类型,而是为了声明一组常量。这时就可以使用下面这种写法,比较简单。
enum { ONE, TWO };
printf("%d %d", ONE, TWO); // 0 1
上面示例中,enum
是一个关键字,后面跟着一个代码块,常量就在代码内声明。ONE
和TWO
就是两个 Enum 常量。
常量之间使用逗号分隔。最后一个常量后面的尾逗号,可以省略,也可以保留。
enum { ONE, TWO, };
由于Enum 会自动编号,因此可以不必为常量赋值。C 语言会自动从0开始递增,为常量赋值。但是,C 语言也允许为 ENUM 常量指定值,不过只能指定为整数,不能是其他类型。因此,任何可以使用整数的场合,都可以使用 Enum 常量。
enum { ONE = 1, TWO = 2 };
printf("%d %d", ONE, TWO); // 1 2
Enum 常量可以是不连续的值。
enum { X = 2, Y = 18, Z = -2 };
Enum 常量也可以是同一个值。
enum { X = 2, Y = 2, Z = 2 };
如果一组常量之中,有些指定了值,有些没有指定。那么,没有指定值的常量会从上一个指定了值的常量,开始自动递增赋值。
enum {
A, // 0
B, // 1
C = 4, // 4
D, // 5
E, // 6
F = 3 // 3
G, // 4
H // 5
}
Enum 的作用域与变量相同。如果是在顶层声明,那么在整个文件内都有效;如果是在代码块内部声明,则只对该代码块有效。如果与使用int
声明的常量相比,Enum 的好处是更清晰地表示代码意图。
评论区