C语言中自定义类型:联合和枚举

在上一篇博客中和大家分享了自定义类型的一种:结构体,这篇和大家聊聊其他的自定义类型:联合和枚举。

1. 联合体

1.1 联合体类型的声明

像结构体?样,联合体也是由?个或者多个成员构成,这些成员可以不同的类型。但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同?块内存空间。所以联合体也叫:共用体。下面我们看这段代码来了解一下:

#include<stdio.h>
union U//联合体声明
{
  char a;
  int b;
};
int main()
{
  union U a={0};//联合变量的定义
  printf("%d
",sizeof(a));//计算联合变量的大小
  return 0;
}

为什么是4呢?这里我们就要提到联合体的特点了:联合的成员是共?同?块内存空间的,这样?个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。我们一起看下面的代码:

#include<stdio.h>
union U
{
  char a;
  int b;
};
int main()
{
  union U s={0};
  printf("%p
",&(s.a));
  printf("%p
",&(s.b));
  printf("%p
",&s); 
  return 0;
}

 

通过上面的代码我们可以知道s的地址为012FFCF4,当我们要取b的地址时发现它也是从这里开始的,b占4个字节即蓝色部分,a也是从这里开始,它占1个字节,即橙色部分。我们发现使用b的时候它4个字节都使用,使用a的时候只使用橙色部分,橙色部分是它们共用的空间。 给联合体其中?个成员赋值,其他成员的值也跟着变化。我们看下面一段代码:

#include<stdio.h>
union U
{
  char a;
  int b;
};
int main()
{
  union U c={0};
  c.b=0x11223344;
  c.a=0x55;
  printf("%x
",c.b);
  return 0;
}

1.2 相同成员的联合体和结构体对比 

我们对比一下相同成员的联合体和结构体的内存布局情况:

struct S
{
  char a;
  int b;
};
struct S a={0};

union U
{
  char a;
  int b;
};
union U a={0};

 

在结构体中,a和b是分开来储存的,在联合体中,a和b共用一块共同的空间,这是他们的区别。 

1.3 联合体的大小计算 

 联合的大小至少是最大成员的大小。当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。那我们一起看下面的代码进行了解:

#include<stdio.h>
union U1
{
  char a[6];
  int b;
};

union U2
{
  short a[7];
  int b;
};

int main()
{
  printf("%d
",sizeof(union U1));
  printf("%d
",sizeof(union U2));
  return 0;
}

这里大家先思考一下U1和U2的内存大小是多少?接着我们调试一下这段代码看一下答案:


 

这里大家是否在疑惑为什么不是6和14呢? char a[6]占6个字节能放下int b啊?别急听我慢慢给大家分析:对于char a[6]的对齐数是怎么算的,它是char类型的数组,它的对齐数是按照它每个成员来算,它每个成员自身大小是1个字节,所以它的对齐数是1,而b的对齐数是4,所以在联合体U1中最大对齐数是4,又因为最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍,所以U1占8个字节,同理U2占16个字节。

1.4 联合体的使用

了解完上面的讲解之后,那联合体有什么用呢?根据联合体的特点我们可以用在a和b不同时用的情况下,比如:我们要搞?个活动,要上线?个礼品兑换单,礼品兑换单中有三种商品:图书、杯?、衬衫。每?种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。

图书:名称、作者、页数

杯子:容量

衬衫:设计、颜色、尺寸

我们用结构写一下看看:

struct gift
{
  int stock_numbers;//库存量
  double price;//定价
  int item_type;//商品类型

  char title[20];//书名
  char author[20];//作者名字
  int num_pages;//页数

  int capacity;//容量

  char design[30];//设计
  int colors;//颜色
  int sizes;//尺寸
}

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

商品如果是图书的话,那就不需要:capacity、design、colors、sizes等信息。

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

struct gitf
{
  int stock_number;
  double price;
  int item_type;
  union
   {
     struct
      {
        char title[20];
        char suthor[20];
        int num_pages;
      }book;
     struct
      {
        int capacity;
      }mug;
     struct
      {
        char design[30];
        int colors;
        int sizes;
      }shirt;
   }item;
}

1.5 练习

写一个程序判断当前机器是大端还是小端,会指针的朋友可能会这样写:

#include<stdio.h>
int main()
{
  int a=1;
  if(*(char*)&a==1)
   {
     printf("小端
");
   }
  else
   {
     printf("大端
");
   }
  return 0;
}

既然在联合体这提到这个问题那就说明联合体也是可以写滴,请看下面的代码:

#include<stdio.h>
int main()
{
  union
   {
     char c;
     int i;
   }u;
  u.i=1;
  if(u.c==1)
   {
     printf("小端
");
   }
  else
   {
     printf("大端
");
   }
  return 0;
}

按照联合体的设计,第一个字节的位置c和i共用,给u.i赋值1,它用4个字节,u.c拿到的是它的第一个字节的位置,所以我们可以通过判断u.c的值来判断大小端。

2. 枚举

2.1 枚举类型的声明

枚举顾名思义就是一一列举,把可能的取值一一列出来。比如:

一周从星期一到星期日是有限的7天,可一一列举出来。

一年有12个月份也可以一一列举出来。

就拿一周来举例,这些数据的表示就可以使用枚举了:

enum day
{
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
};

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

enum color
{
  red=2,
  green=7,
  blue=5
};

2.2 枚举类型的优点 

为什么使用枚举呢?我们也可以使用#define定义常量,为什么非要使用枚举?枚举的优点:

1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 便于调试,预处理阶段会删除 #define 定义的符号
4. 使用方便,?次可以定义多个常量
5. 枚举常量是遵循作?域规则的,枚举声明在函数内,只能在函数内使用 

这里我们举个例子:用代码实现计算器(因为之前写了好多次了,这里就不详细写了,只是想用这段代码让大家了解枚举的优点)

#include<stdio.h>
void menu()
{
  printf("******************
");
  printf("*** 1.add 2.sub***
");
  printf("******************
");
}
enum option
{
  add=1,
  sub=2
};
int main()
{
  int input=0;
  do
   {
     menu();
     printf("请选择:
");
     scanf("%d",&input);
     switch(input)
      {
        case add:  //这里用add表示会比用1表示可读性会高
          break;
        case sub:  //同理
          break;
      }
   }
  return 0;
}

本篇博客到这里就结束啦,大家有问题可以评论出来或者私信我哦。