结构体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。