自定义类型-结构体-位段-枚举-联合体

自定义类型:结构体,位段,枚举,联合体

  • 1.结构体
    • 声明一个结构体类型
    • 声明变量
    • typedef 重命名
    • 结构体的特殊声明
    • 结构体变量的初始化
    • 结构体大小的计算
    • 修改默认对齐数
    • 计算偏移量
  • 2.位段
    • 位段的声明
    • 位段的内存分配
    • 位段的跨平台问题
  • 3.枚举
    • 枚举类型的定义
    • 枚举的使用
    • 枚举的优点
  • 4.联合体(共用体)
    • 联合体的声明
  • 联合体的介绍

有关结构体,位段,枚举,联合体的介绍


1.结构体

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

声明一个结构体类型

声明一个学生类型,属性:名字+电话+年龄。

struct stu
{
	char name[10];
	char telnumber[30];
	int age;
};


声明变量

struct stu
{
	char name[10];
	char telnumber[30];
	int age;
}wuhu;//全局变量

struct stu3;//全局变量
int main()
{
	struct stu stu1;//局部变量
	struct stu stu2;//局部变量
	return 0;
}

typedef 重命名

typedef struct stu
{
	char name[10];
	char telnumber[30];
	int age;
}wuhu;//typedef将stu重命名为wuhu

结构体的特殊声明

  • 1.匿名结构体类型
struct
{
	char a[20];
	int b;
}x;//只能使用结构体声明后创建的变量,不能创建新的变量
  • 2.结构体的自引用
struct A
{
	int a;
	struct A a;//err 这里会无限套娃
};
struct B
{
	int a;
	struct B* next;//结构体的自引用只能是指针
};

结构体变量的初始化

typedef struct stu
{
	char name[10];
	char telnumber[30];
	char a;
	int age;
}wuhu;
int main()
{
	wuhu a = { "abcde","1234",'a',10 };
	return 0;
}


结构体大小的计算

要计算结构体的大小首先要了解结构体的对齐规则

  • 1.成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
  • 2.对齐数=编译器默认的第一个对齐数(vs中默认对齐数为8) 与 该成员变量的大小中的较小值。
  • 3.结构体大小为最大对齐数的整数倍。
  • 4.如果出现嵌套了结构体的情况,嵌套结构体对齐到自己的最大整数倍数。结构体整体的大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍数。

例如:

typedef struct stu
{
	char name[10];
	char a;
	int age;
}wuhu;

上述结构体类型中char name[10]可以看成10个char类型,在32位平台中一个char占1个字节 vs 中默认对齐数位8,1和8对比1比较小对齐数取1,char name[10]占10个字节a也是char所以也占一个字节 。 age为int类型对齐数为4所有只能放在4为倍数的地址处,而name[10]与a加起来占11个字节不是4的倍数所以age只能放在12地址处所以总大小为12+4=16 16 16是4的倍数所以wuhu的大小为16。


为什么存在内存对齐?
大部分的参考资料都是这么说的(没有官方解释)

  • 1.平台原因(移植原因):不是所有硬件平台都 能访问任意地址上的任意数据;
    某些硬件平台只能在某些地址处取某些特定类型的数据否则抛出硬件异常。
  • 2.性能原因:数据结构(尤其是栈)应该尽可能的在自然界边界上对齐。原因在与
    为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存
    访问仅仅需要一次访问。
  • 3.总体来说:结构体的内存对齐是拿空间来换取时间的做法。

所以当我们声明结构体时为了减少浪费让占用空间小的尽量集中在一起。


修改默认对齐数

代码如下

#pragmaticpack(4);//设置对齐数为4
#pragmaticoack();//取消设置的默认对齐数

计算偏移量

代码如下

#include
offsetof(struct S,c);//struct S为定义类型,c是需要计算偏移量的元素。

2.位段

位段的介绍:
1.位段的成员必须是int,unsigned int,或signed int
2.位段的成员名后面的有一个冒号和一个数字。


位段的声明

struct A
{
	int a : 2;
	int b : 5;
	int c : 10;//分号后面是内存分配 大小是字节
	int d : 30;
};

位段的内存分配

  • 1.位段的成员可以是 int,unsigned int,signed int 或者是 char (属于整形家族)类型。
  • 2.位段的空间上是按照需要以4个字节(int)或者(char)的方式来开辟的。
  • 3.位段涉及很多不确定因素,位段是不跨平台的,注意可移植的程序应该避免使用位段。
    自定义类型-结构体-位段-枚举-联合体_第1张图片

位段的跨平台问题

  • 1.int位段被当成有符号数还是无符号数是不确定的。

  • 2.位段中最大位的数目不能确定,(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题)。

  • 3.位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

  • 4.当一个结构体包含两个位段,第二个位段成员比较大,无法容纳于第一个位段的位时,是舍弃剩余的位还是利用,这是不确定的。

  • 总结:和结构体相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。


3.枚举

枚举就是一一列举,如星期有星期1到星期天,月份有1~12月。

枚举类型的定义

enum Sex
{
	MALE = 2,
    FEMALE = 3,
	SECRET=0
};

上述代码中enum为枚举的关键字 Sex为枚举类型,MALE, FEMALE, SECRET为枚举可能的取值,可赋值(但值不可修改)也可以不赋值(不赋值默认第一个为0后面依次加1)。


枚举的使用

enum Sex
{
	MALE = 2,
    FEMALE = 3,
	secret=0
};
int main()
{
	enum Set s = MALE;//只能赋值MALE,FEMALE,AECRET
	return 0;
}


枚举的优点

为什么使用枚举?
我们可以使用#define定义常量,为什么要用枚举?

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

4.联合体(共用体)

联合体的声明

union Un
{
	char i;
	int j;
};

上述代码中 union 为联合体的关键字 Un为类型i和j不能同时存在。


联合体的介绍

  • 1.联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以也叫共用体)。
  • 2.联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
  • 3.联合体的大小至少是最大成员的大小当最大成员大小不少最大对齐数的倍数的时候就要对齐到最大对齐数的整数倍数处。

你可能感兴趣的:(c语言)