结构体枚举与联合

结构体struct

- 基本介绍
struct是结构体的关键字,它将一些相关联的数据打包成一个整体,这些相关联的数据可以是不同的类型。它们被称为结构体的成员,结构体的成员可以是标量、数组、指针、或者是其他结构体。
- 结构的声明
假设我们要建立学生档案,要纳入学生的姓名,年龄和性别三个内容。那么我们可以通过建立一个结构体来实现这个过程。

struct student
{
  char name[20];
  int age;
  char sex[5];
};

这样一个学生信息的结构体就声明完成了。其中包含了我们需要的姓名,性别和年龄三个成员。

- 结构体成员的访问
声明了结构体之后,接下来就是使用了。我们对于结构体成员的访问可以通过“.”点操作符或者指针来实现。例如上述学生的结构体,我们现在试着来使用点操作符来访问成员。

struct student s; //这里创建了一个结构体变量
s.age = 10; //通过点操作符给s这个变量里年龄成员赋值
strcpy(s.name,"jacky"); //同样通过点操作符给s变量的name成员赋值

上面是通过点操作符来实现对结构体变量实现成员访问,下面我们来试一试通过指针的方式来访问结构体的成员。

struct student s;
struct student *p = s;   //通过指针指向该结构体变量
p->age = 10;             //通过指针加箭头的方式访问age成员
strcpy(p->sex,"male");   //通过指针访问sex成员 

- 结构体的大小计算
提到结构体的大小计算,就必须要理解结构体的内存对齐规则:
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字的整数倍的地址处,这个数字被称为对齐数。对齐数为编译器默认的一个值(vs中默认为8,Linux中默认为4)与该成员自身大小的较小值。
3. 结构体总大小为所有成员中最大对齐数的整数倍。
4. 如果嵌套了结构体,那么嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有成员中(包含嵌套的结构体)最大对齐数的整数倍。
那么为什么会有内存对齐原则呢,主要是因为(1)不是所有的硬件平台都可以访问任意地址上的任意数据。(2)处理器如果访问未对齐的内存需要做两次访问,如果访问对齐的内存只需要一次访问。
总的来说就是虽然浪费了一部分空间,但是可以提高处理速度。
接下来我们通过实例来感受结构体大小的计算。

struct C1
{
  char a;
  int b;
  char c;
};
struct C2
{
   char a;
   char b;
   int c;
};

根据我们上面讲的对齐原则,可以算出C1的大小是12个字节,C2的大小是8个字节,而他们的成员个数和大小都是一样的,只是因为位置不一样,所以由此我们可以看出在声明结构体的时候,我们可以尽量把占内存比较小的成员集中在一起,这样就有可能在一定程度上节省内存。

位段

跟结构体相比,位段可以达到同样的效果,但是可以更好得节省空间,但是有跨平台的问题存在。位段的声明和结构体是比较类似的,但是有两点不同:
1. 位段的成员必须是int,unsigned int,signed int或者char类型。
2. 位段的成员名字后面必须有一个冒号和一个数字。
下面举一个位段的例子:

struct A
{
int a:2;
int b:5;
int c:20;
int d:20;
};

A就是一个位段类型,那么位段的大小该如何让计算呢,每个成员冒号后面的数字就是这个成员占byte的大小,位段是按照需求来进行4个字节(int)或者1个字节(char)的开辟,直到能够放下当前成员。所以按照这个方法来计算的话,struct A占用了8个字节的大小。

- 位段的跨平台问题
1. int位段被当作有符号数还是无符号数是不确定的。
2. 位段中的最大位的数目不能被确定。
3. 位段中的成员在内存中从左向右还是从优向左分配的标准没有定义。
4. 当一个结构包含两个位段,第二个位段成员较大,无法容纳于第一个位段剩余的位时,是舍弃还是利用剩余的位,这是不确定的。

枚举 enum

枚举的意思就是把有可能的事件都一一列举,例如一周的7天,一年的四季,人的性别等,都可以使用枚举来定义。
接下来我们以星期为例来定义一个枚举:

enum day
{
   Mon,
   Tues,
   Wed,
   Thur,
   Fri,
   Sat,
   Sun
};

这里的enum就是一个枚举类型,{ }中的内容是该枚举类型的可能取值,也叫枚举常量。这些枚举常来那个都是有值的,默认从0开始,一次递增1,在定义时也可以赋初值,举例如下;

enum Color
{
   red = 1,
   green = 3,
   blue = 5
}

- 枚举的优点
1. 增加代码的可读性和可维护性。
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染。
4. 便于调试。
5. 使用方便,可以一次定义多个常量。

联合 union

联合也是一种自定义类型,它定义的变量包含一系列的成员,所有的成员共享同一块空间。所以联合也叫公用体。

- 联合的声明

union N
{
   char c;
   int i;
};

联合的声明与结构体类似,都是包括成员和联合体的名字。联合体的成员公用一块空间,所以一个联合体的大小至少是最大成员的大小。
当联合体的大小不是最大对齐数成员对齐数的整数倍时,就要对齐到最大对齐数的整数倍。例如:

union n1
{
   char c[5];
   int a;
};
union n2
{
   char c[7];
   int i;
}
printf("%d\n",sizeof(union n1));
printf("%d\n",sizeof(union n2));

输出结果是 4 和 8 ;
这里IU可以看出共用体大小的计算方式了。

- 联合的实际应用
为了加深对联合的成员共用同一片空间的理解,接下来我们试一试使用联合来看一个机器的大小端。

int check()
{
    union n1
    {
        char c;
        int i;
    }n1;
    n1.i = 1;
    return n1.c;
}
int main()
{
    if (check() == 1)
        printf("是小端\n");
    else
        printf("是大端\n");
    return 0;
}

因为在联合体中c和i共用同一块空间,有同一块起始地址,所以修改了i之后,c也会跟着被改变,因为i是整型占用4个字节,c占用1个字节。如果机器是小端存储,那么高位存低地址,低位存高地址。当i=1时,c也是1。

猜你喜欢

转载自blog.csdn.net/higher_and/article/details/80346651