0%

C语言中的自定义类型(结构体、枚举及联合)

结构体

结构体是一些值的集合,这些值被称为成员变量,结构的每个成员可以是不同类型的变量。

结构体的声明

1
2
3
4
5
6
7
8
9
10
11
struct piont
{
int x;
int y;
}p1;//声明结构体类型,同时定义变量p1

struct point p2;//定义结构体变量p2

struct point p3 = {x, y};//初始化:定义变量同时赋值

struct point* p4;//定义结构体类型指针

结构体在声明的时候可以省略结构体标签(tag),称之为匿名结构体,但很少会用到。
结构体可以使用 \{ \}初始化,但不能用它赋值。

结构体的成员访问

结构体变量访问成员,通过(.)操作符来访问。
例如:

1
2
p1.x = 20;
p1.y = 10;

结构体指针访问指向变量的成员,使用( -> )操作符来访问
例如:

1
printf("%d,%d\n", p4->x, p4->y);

结构体可以自引用,但是只能通过包含自身类型的指针这种方式来自引用。

结构体的内存对齐

结构体的内存对齐规则:

  1. 第一个成员在与结构体变量偏移量为 0 的地址处。
  2. 其他成员变量要对齐到某个数字(称之为对齐数)的整数倍地址处。
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

对齐数: 编译器默认的一个对齐数,与该成员大小的 较小值


  • VS中默认的对齐数值为8。
  • Linux没有默认对齐数,它的默认对齐数等于当前字段的sizeof值。
  • 可以用 #pragma pack ( )来修改默认对齐数,如果括号内不填写数字,则 还原成默认对齐数。

_举例一:_
(VS2013下)

1
2
3
4
5
6
7
struct s{
char a;//1 字节
int b;//4 字节
double c;//8 字节
char d;//1 字节
short e;//2 字节
};

对于该结构体类型来说,它占据内存空间大小的计算方法是:

  1. 根据第一条规则,对第一个成员变量,它在与结构体变量偏移量为0的地址处,此时算上一个字节。
  2. 根据第二条规则,对第二个成员,它是int类型变量,4个字节,与最大对齐数8相比,4更小,所以该成员的对齐数是4,要放在4的整数倍地址处,此时第一个成员占据的第一个字节之后的三个字节被开辟,在距离结构体变量偏移为4的地址处算四个字节,此时结构体变量有八个字节。
    前两个成员

前两个成员

  1. 根据第二条规则,对于第三个成员,double类型,八个字节,与默认对齐数相等,所以对齐数取8,它放在8的整数倍地址上,即放在距离结构体变量偏移为8的地址处,此时结构体变量有16个字节。
    前三个成员

前三个成员

  1. 根据第二条规则,第四个成员,char类型,一个字节,同上,它的对齐数是 1,所以直接放置即可,此时结构体变量有17个字节
  2. 根据第二条规则,第五个成员,类型是short,两个字节,同上,它的对齐数是2,放在地址是二的整数倍处,即第18个字节处,此时结构体变量为20个字节
    所有成员

所有的五个成员

  1. 根据第三条规则:结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍,这个结构体的最大对齐数是8,那么它占据的内存应该是八的整数倍,此时20个字节,补齐到24个字节即为八的整数倍
    结构体全貌

所以,在举例一种,该结构体的大小为24个字节。

_举例二:_
求此时struct s2 结构体类型的大小

1
2
3
4
5
6
7
8
9
10
11
12
13
struct s{
char a;
int b;
double c;
char d;
short e;
};
struct s2{
char a;
struct s b;
char c;
int d;
};
  1. 根据第一条规则,对第一个成员变量,它在与结构体变量偏移量为0的地址处,此时算上一个字节。
  2. 根据第四条规则,如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍,结构体s中的最大对齐数是8,那么就要对齐到八的整数倍地址处,此时第一个字节往后开辟七个字节之后,来存放该结构体类型变量,这个结构体类型变量的大小在举例一中计算过,24个字节,所以此时s2为32个字节。
    前两个成员

前两个成员

  1. 根据第二条规则,第三成员是char类型,1个字节,对齐数为1,所以此时结构体s2为33个字节。
  2. 根据第二条规则,第四成员为int类型,四个字节,对齐数为4,对齐到4的整数倍地址处,即第36个字节处,此时结构体大小为40字节。
  3. 根据第三条规则,该结构体各个成员中最大对齐数是八,所以它占据的内存应为八的整数倍,而40正好是八的整数倍,所以不用补齐

所以举例二中结构体类型的大小应为40字节。

位段

位段的声明和结构体类似,但是位段的成员必须是int 、unsigned int或signed int,位段的成员后有一个冒号和一个数字。冒号后的数字是几,就说明前边的成员变量占几个比特位

1
2
3
4
5
6
struct A
{
int a : 1;
int b : 3;
int c : 5;
};

枚举

将可能的取值一一列举,即为枚举

1
2
3
4
5
6
7
enum season
{
spring,
summer,
autumn,
winter
};

枚举常量的值默认由0开始依次递增,除非在声明中赋值

联合

该类型定义的变量包含一系列成员,但是这些成员都公用同一块内存空间

1
2
3
4
5
union Un
{
int a;
char b;
};

可以用它的特性来判断大小端字节序,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
union Un
{
int a;
char b;
};
int main()
{
union Un s;
s.a = 0x11223344;
printf("%x\n", s.b);
system("pause");
return 0;
}//输出为44,当前机器为小端字节序。

联合的大小:

  • 联合的大小至少是最大成员的大小
  • 当最大成员的大小不是最大对齐数的整数倍时,要对齐到最大对齐数的整数倍上。
-------------本文结束感谢您的阅读-------------