联合

Q:联合是什么?
A:联合(union) 是一种数据类型,它可以在同一内存空间中存储不同的数据类型(不是同时存储)。

PS:结构是可以同时存储不同数据类型的,但联合不可以,只能存储某一个数据类型。

关键字:union。

联合的声明

联合的声明和结构声明类似,使用 union 关键字进行声明。

  1. union hold {
  2. int digit;
  3. double bigfl;
  4. char letter;
  5. };

hold 是联合的标记,之后可以使用 hold 声明一个联合变量。

  1. union hold var1;// hold 类型的联合变量 var1

也可以在声明联合模板的时候创建一个联合变量。

  1. union hold {
  2. int digit;
  3. double bigfl;
  4. char letter;
  5. } var2;

联合变量的内存空间:联和模板中占用空间最大的类型的内存大小。如,上例中的 int、double、char 中 double 的内存大小最大,8 字节,所以 union hold 变量的内存空间是 8 字节。

联合的初始化

与结构类似,通过初始化列表来初始化联合对象。但是,对联合来说,列表只有一个初始化器。C99 允许在初始化器中使用成员指示符来指示哪个成员被初始化。而且,如果初始化器没有成员指示符,那么就与联合内的第一个成员关联。具有自动存储类的联合对象也可以使用已有的同类型对象来初始化。

联合的使用

使用点运算符表示正在使用哪种数据类型。在联合中,一次只能存储一个值。即使有足够的空间,也不能同时存储两个类型的值。

  1. fit.digit = 23; // 把23存储在fit中,占2字节
  2. fit.bigfl = 2.0; // 清除23,存储2.0,占8字节
  3. fit.letter = 'h'; // 清除2.0,存储h,占1字节

联合的大小

联合的大小是其最大成员的大小。如果成员是数组的话,成员的大小为数组的大小。

  1. union Std {
  2. int a;
  3. char b;
  4. double c;
  5. } s1;
  6. // Std 的大小为 double 的大小,即 8.
  7. union Stu {
  8. int a;
  9. char b[10];
  10. double c;
  11. } s2;
  12. // 最大成员为 char 数组,大小为 10.
  13. // 但是需要考虑字节补齐。如果编译器默认对齐数是4,大小为12.如果编译器默认对齐数是8,大小为16.

练习

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. union {
  4. struct {int x, y;} s;
  5. int x, y;
  6. } u;
  7. int main()
  8. {
  9. printf("addr:&u.x=%p, &u.y=%p, &u.s.x=%p, &u.s.y=%p\n\n", &u.x, &u.y, &u.s.x, &u.s.y);
  10. u.x = 1;
  11. printf("val:u.x=1;\nres:u.x=%d, u.y=%d, u.s.x=%d, u.s.y=%d\n\n", u.x, u.y, u.s.x, u.s.y);
  12. u.y = 2;
  13. printf("val:u.y=2;\nres:u.x=%d, u.y=%d, u.s.x=%d, u.s.y=%d\n\n", u.x, u.y, u.s.x, u.s.y);
  14. u.s.x = u.x * u.x;
  15. printf("val:u.s.x = u.x * u.x;\nres:u.x=%d, u.y=%d, u.s.x=%d, u.s.y=%d\n\n", u.x, u.y, u.s.x, u.s.y);
  16. u.s.y = u.y + u.y;
  17. printf("val:u.s.y = u.y + u.y;\nres:u.x=%d, u.y=%d, u.s.x=%d, u.s.y=%d\n\n", u.x, u.y, u.s.x, u.s.y);
  18. getchar();
  19. return 0;
  20. }

4.5 联合和枚举 - 图1

枚举

Q:枚举是什么?
A:枚举类型声明符号名称来表示整型常量。
关键字: enum。

  简单来说,枚举就是声明了一个符号名称来作为整型常量使用,它将一些整型常量用有意义的字符来表示,本质上依旧是使用整型常量,只是更易于阅读代码。
  枚举类型的目的也就是提高程序的可读性。

枚举的声明

枚举的声明和结构、联合的声明语法类似。如下列声明创建了 color 作为标记名,允许将 enum color 作为一个类型名使用。

  1. enum color {
  2. red,
  3. orange,
  4. yellow,
  5. green,
  6. blue
  7. };

花括号中的标识符列举了 enum color 变量可能的值。因此 enum color 类型的变量的值可能是 red、orange、yellow…
那么 red、orange、yellow… 又代表什么值呢?

枚举的值

默认值

默认情况下,枚举列表中的常量会被赋予0、1、2…,从0开始依次递增。

  1. enum color {
  2. red,
  3. orange,
  4. yellow,
  5. green,
  6. blue
  7. };

例如,上面的例子实际上就是默认 red 代表 0,orange 代表 1,yellow 代表 2,green 代表 3,blue 代表 4。

赋值

在枚举声明中除了使用默认值,我们还可以指定整数值。如果只给一个枚举常量赋值,没有对后面的枚举常量赋值,那么后面的常量会被赋予后序的值。

  1. enum color {
  2. red,
  3. orange = 10,
  4. yellow,
  5. green = 20,
  6. blue
  7. };

在声明的声明中,red 代表 0,orange 代表 10,yellow 代表 11,green 代表 20, blue 代表 21。

虽然这样语法上也是 OK 的,但是推荐使用枚举的时候,每个枚举常量都进行显式的赋值。

枚举的大小

枚举的大小就是 int 类型的大小,即 4 字节。

枚举变量

  1. enum color {
  2. red,
  3. orange = 10,
  4. yellow,
  5. green = 20,
  6. blue
  7. };
  8. int main(void){
  9. enum color col1 = red;
  10. printf("%d\n", col1); // 输出 0
  11. enum color col2 = blue;
  12. printf("%d\n", col2); // 输出 21
  13. printf("%d\n", sizeof col1); // 输出 4
  14. return 0;
  15. }

小结

  1. 在没有显示说明的情况下,枚举常量(也就是花括号中的常量名)默认第一个枚举常量的值为0,往后每个枚举常量依次递增1
  2. 在部分显示说明的情况下,未指定的枚举名的值将依着之前最有一个指定值向后依次递增
  3. 一个整数不能直接赋值给一个枚举变量,必须用该枚举变量所属的枚举类型进行类型强制转换后才能赋值
  4. 同一枚举类型中不同的枚举成员可以具有相同的值
  5. 同一个程序中不能定义同名的枚举类型,不同的枚举类型中也不能存在同名的枚举成员(枚举常量)

    名称空间

    C 语言通过名称空间标识程序中的各部分,即通过名称来标识。作用域是名称空间概念的一部分:两个不同作用域的同名变量不冲突。

名称空间是分类别的,在特定作用域中的结构标记、联合标记、枚举标记共享相同的名称空间,但是该名称空间和普通变量使用的名称空间不同。 这表示相同作用域中的变量和标记的名称可以相同,并不会引起冲突。

PS:在 C++ 中不允许这样做,因为它把标记名和变量名放在同一名称空间中。