目录
1.结构体(struct)
1.1结构体类型的声明
1.2结构体的嵌套
1.3结构体变量的定义和初始化
1.4结构体内存对齐
1.5结构体传参
2.枚举(enum)
2.1枚举类型的定义
2.2枚举的优点
2.3枚举的使用
3.联合(union)
3.1联合类型的定义
3.2联合的特点
3.3联合大小的计算
温馨提示:
? ? ? ? 由于本人实力有限,难免有错误的地方,如果就此误导了里您,望海涵并恳请指正;如果您觉得有用那皆大欢喜
1.结构体(struct)
1.1结构体类型的声明
结构体是是一些值的集合,这些值被称为成员变量;
//描述一个学生
struct Student
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};//分号不能丢
//typedef的使用
typedef struct Student
{
char name[20];
int age;
char sex[5];
char id[20];
}Stu;//Stu就具有了结构体的属性
//Stu == struct Student
//有匿名结构体这一说法,顾名思义,省去了结构体标签(如Student);不建议这么用
1.2结构体的嵌套
1.2.1结构体自引用
在结构体中,包含了一个类型为该结构体本身的成员
struct Student
{
char name[20];
int age;
char sex[5];
char id[20];
struct Student* a;
};//Student 中包含了一个成员a;
1.2.2结构体嵌套
struct Birthday
{ //声明结构体 Birthday
int year;
int month;
int day;
};
struct Student
{ //声明结构体 Student
char name[20];
int num;
float score;
struct Birthday birthday;
};//结构体Student 嵌套 结构体Birthday
1.3结构体变量的定义和初始化
1.3.1结构体变量的定义
struct Student
{
char name[20];
int age;
char sex[5];
char id[20];
}stu1;//在结构体Student声明的同时定义了结构体变量stu1;
//或者//声明之后定义
struct Student
{
char name[20];
int age;
char sex[5];
char id[20];
};
struct Student stu1;
1.3.2结构体变量的初始化
{}括起来,每个成员用 , 隔开
//定定义后直接赋值
struct Student
{
char name[20];
int age;
char sex[5];
char id[20];
}stu1={"帅狗",20,MALE,1234567};
//或者
struct Student
{
char name[20];
int age;
};
struct Student stu2={"小小怪",12};
//
//通过结构体访问成员赋值
stu2.name="小小怪";
stu2.age=8;
1.4结构体内存对齐
1.4.1结构体内存对齐的规则
-
第一个成员在与结构体变量偏移量为0的地址处。 -
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8 -
结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。 -
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
1.4.2结构体内存对齐存在的原因
移植原因
- 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
性能原因:
- 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
????????原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
总体来说:
????????结构体的内存对齐是拿空间来换取时间的做法。
1.4.3结构体内存大小的计算
#include<stdio.h>
int main()
{
//练习1
struct S1
{
char c1;//对齐数为1,对齐到1,占第1个字节
int i;//对齐数为4,应对齐到4的倍数,4或者8,而对齐到4地址处显然不够用(因为第一个字节背c1用了,对齐到4的话只有3个字节可用,显然不行),只有对齐到8占第5-8字节
char c2;//对齐数为1,对齐到9,占第9个字节
};
//最大对齐数是4,目前用了9个字节空间,不是4的整数倍,自动补齐3个字节,所以S1大小应是12个字节
printf("%d\n", sizeof(struct S1));//12
//练习2
struct S2
{
char c1;//占第1个字节
char c2;//占第二个字节
int i;//占5-8个字节
};
//最大对齐数是4,目前占用8(4的倍数)个字节,所以S2大小为8;
printf("%d\n", sizeof(struct S2));//8
//练习3
struct S3
{
double d;//(0地址处)占8个字节
char c;//占第9个字节
int i;//占13-16个字节
};
//最大对齐数为8,目前占用16(4的倍数)个字节,所以S3大小为16
printf("%d\n", sizeof(struct S3));//16
//练习4
struct S4
{
char c1;(0地址处)占第1个字节
struct S3 s3;//这是结构体嵌套,所以s3对齐到(它自己最大对齐数为8,对齐到8或者16都不够用,只能是24)24,占9-24个字节
double d;占25-32个字节
};
//S4最大对齐数为8,目前占用32(8的倍数)个字节,所以S4大小为32
printf("%d\n", sizeof(struct S4));//32
return 0;
}
注意:
????????一个结构体中,两个成员之间的有些字节并没被使用,但也是开辟了空间的。 如S1中的c1 和 i之间有3个字节未使用,虽然这些字节并没有拿去使用,编译器还是会开辟的空间,这也就导致了内存的浪费。
????????为了解决或者减少这种浪费 可以让对齐数小的成员放前面----先定义字节占用小的类型//还可以修改最大对齐数----#pragma pack(1)把默认最大对齐数改为1
1.5结构体传参
????????结构体传参请传结构体的地址
struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); ?//传结构体
print2(&s); //传地址
return 0;
}
print2优于print1
- 函数传参的时候,参数是需要压栈,会有时间和空间 的系统开销。
- 传递一个结构体对象的时候,如果结构体过大,参数压栈的系统开销就比较大,导致性能的下降。
2.枚举(enum)
????????枚举就是把有可能的取值一一列举出来
2.1枚举类型的定义
举例
enum Sex//性别
{
MALE,//男 对应值为0;
FEMALE,//:女 1;
SECRET,//保密 2;
}
enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=3
};
2.2枚举的优点
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 防止了命名污染(封装)
- 便于调试
- 使用方便,一次可以定义多个常量
实例:通讯录小程序菜单的设置,巧用枚举
2.3枚举的使用
enum Color//颜色
{
RED=1,
GREEN=2,
BLUE=4
};
enum Color clr = GREEN;
//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
clr = 2; ?//这种错误的,类型不统一不能赋值,一个是枚举变量,一个是整形(Python里允许这么用)
3.联合(union)
3.1联合类型的定义
- 和结构体很像,除了类型不同,声明格式和变量定义和赋值的格式都是一样的
3.2联合的特点
- 联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)
- 所有成员相对于联合体首地址偏移量都为0;(请区别于结构体)
- 同一时间只能储存1个被选择的变量,对其他成员变量会覆盖原变量
3.3联合大小的计算
?回顾:对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(每个成员都有一个对齐数) VS中默认的值为8
#include<stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
union Un un;
int main()
{
printf("%d\n", sizeof(un));//4就是显而易见了
return 0;
}
#include<stdio.h>
union Un1
{
char c[5];//占前5个字节,对齐到5
int i;//对齐数为4,前5个字节已被占用,应该对齐8,占5-8个字节(和c[4]有内存重叠),Un1大小为8;
};
union Un2
{
short c[7];//占前14个字节,对齐到16
int i;//占13-16个字节,对齐到16(和c[6]有内存重叠),Un2大小为16
};
int main()
{
//下面输出的结果是什么?
printf("%d\n", sizeof(union Un1));//8
printf("%d\n", sizeof(union Un2));//16
return 0;
}
|