1.1结构体的基础知识
结构体是一些值的集合,这些值被称为成员变量。结构体的成员变量可以是不同类型的。
1.2:结构体的声明
struct tag//tag:结构体名
{
member-list//member-list:结构体成员变量
}variable-lisr;//variable-lisr:结构体变量名
例如:描述一个人
struct Peo{
char name[20];//人名
int age;//年龄
char id[18];//身份证
}peo;//分号不能丢
1.3:结构体特殊的声明
在声明结构体的时候,可以不完全声明,被称为匿名结构体类型,该结构体只能在结构体分号前创建变量,并且该匿名结构体只能使用一次。
样例:
struct
{
char name[20];
int age;
char id[18];
}s1 = {"zhangsan",18,"123456789"};
int main()
{
printf("%s %d %s", s1.name, s1.age, s1.id);
return 0;
}
匿名结构体即使内部结构体变量完全相同,但编译器任会认为它们两个声明是不相同的类型
样例:
struct
{
char name[20];
int age;
char id[18];
}s1;
struct
{
char name[20];
int age;
char id[18];
}*s2;
int main()
{
s2 = &s1;
return 0;
}
1.4:结构体的自引用
结构体自引用,可以在结构体内创建一个同类型的结构体指针,根据该指针找到下一个结点
struct Node{
int age;
struct Node* next;//同类型结构体指针
};
int main()
{
struct Node n;
return 0;
}
1.5结构体变量的定义和初始化
(1)创建全局变量方法与初始化
//方法一:
struct Stu
{
int data;
char id[20];
}s1 = {18,"123456789"},s2;//全局变量
//定义变量时可以同时初始化或者不初始化也许
//方法二:
struct Stu
{
int data;
char id[20];
};
struct Stu s3 = { 20, "123478569" };//全局变量
struct Stu s4;
//定义变量时可以同时初始化或者不初始化也许
(2)创建局部变量方法与初始化
struct Peo
{
int age;
char name[20];
};
int main()
{
struct Peo p = { 18, "zhangsan" };//局部变量初始化
struct Peo p1;//局部变量
struct Peo p2={.name="zhangsan",.age=20};//通过.成员名可以按照自己的方式初始化赋值
}
1.6结构体内存对齐
(1):结构体内存对齐规则
1、第一个成员直接对齐到结构体变量起始为0的偏移处4,可用offsetof查看对其数位于起始位置的哪个偏移量
#include<stdio.h>
#include<stddef.h>
struct Stu
{
int a;
char b;
float c;
};
int main()
{
struct Stu s;
printf("%d\n", offsetof(struct Stu, a));
printf("%d\n", offsetof(struct Stu, b));
printf("%d\n", offsetof(struct Stu, c));
printf("%d\n", sizeof(s));
}
2、第二个成员要对齐到某个对齐数的整数倍的偏移处
对齐数:结构体成员自身大小和默认对齐数的较小值
vs默认对齐数:8
linux环境不设对齐数(对齐数就是结构体成员的自身大小)
3、结构体的总大小必须是最大对齐数的整数倍(最大对齐数就是每个结构体都有一个对齐数,其中最大的对齐数就是最大对齐数)
4、结构体中如果嵌套了一个结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
#include<stdio.h>
#include<stddef.h>
struct Stu
{
int a;
char b;
float c;
}s;
struct S
{
int a;
struct Stu s;
char b;
};
int main()
{
printf("%d\n", offsetof(struct S, a));
printf("%d\n", offsetof(struct S, s));
printf("%d\n", offsetof(struct S, b));
printf("%d\n", sizeof(struct S));
}
1.7修改默认对齐数
#pragam pack(num)//num为要修改的默认默认对齐数
#pragam pack()//将修改的默认对齐数取消,还原为原来的默认对齐数
1.8结构体传参
形参是实参的临时拷贝,函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销,如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。因此结构体传参时尽量选择址传参,这样可以减少时间和空间浪费
2.1位段是什么
1、位段的声明和结构体类似,但是位段的成员必须时 int、
unsiged int、signed int、char类型的
2、位段的成员后边有一个冒号和一个数字,冒号后边的数字是表示该成员的变量的大小(单位:bit)
3、C语言中没有专门的位段类型,位段的定义要借助于结构体,即以二进制位为单位定义结构体成员所占存储空间
struct Stu
{
int _a : 2;//开辟2bit空间大小
int _b : 3;//开辟3bit空间大小
int _c : 16;//开辟16bit空间大小
int _d : 30;//开辟30bit空间大小
};
int main()
{
printf("%d\n", sizeof(struct Stu));
}
2.2位段的内存分配
1、位段的成员可以是int、unsigned in、singned int或者char类型。位段的空间上是按照需要以4字节(int)或者1个字节(char)的方式开辟的
2、位段涉及很多不确定因素,位段是不跨平台,注重可移植的程序应该避免使用位段
3、不允许位段跨越一个字节的边界,如果一个字余下的空间不能容纳一个位段,则这个位段从相邻的下一个字的边界开始存放。由此在上一个字中留下未用的空位,称为空穴。(16位系统一个字是2个字节,32位系统一个字是4个字节,64位系统是8个字节。)
样例:vs2013平台测试
a的二进制为1010,但由于a只有3bit空间只能存放3位,因此a的值为2
b的二进制为1100,b有4bit空间,但是最高位被定义为符号位,数据位就为100,因此b的值为-4
c、d都由于开辟的空间不够存放所以都需要重新开辟一块空间存放,未使用的区域则就浪费了
4、位段的特点
●位段可以没有名字,无名位段表示该空间不用。
●位段只能作为结构体成员,不能作共用体的成员。
●位段没有地址,对位段不能进行取地址的“&”运算。
●位段的长度不能大于机器字长度,也不能定义位段数组。
●位段可以用整型格式符输出。
●位段可以在数值表达式中引用,它会被系统自动转换成int型。
5、位段与结构相比,位段也可以达到同样的效果,但是位段可以更好的节省空间,但存在跨平台问题
2.3位段的跨平台问题
1、int 位段被当成有符号数还是无符号数是不确定的
2、位段中最大位的数目不能确定(16位机器最大16位,32位机器最大32位,若在16位机器中写成20则就会发生报错)
3、位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义
4、当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的
3.1枚举是什么
枚举顾名思义就是一 一列举,把可能的取值一 一列举,在C/C++中枚举是一个被命名的整型常数的集合。可以用枚举表示的例如一周的周一到周日,月份等等
3.2枚举类型的定义
1、枚举关键字enum,枚举与结构体类似但是枚举的成员是以“,”逗号分隔的,结构体成员变量是以“;”分号分隔的,枚举的成员变量是没有类型的,最后一个成员变量可以 不用“,”
enum month//enum month:是枚举类型;month:是枚举名
{
mon,
tues,
wed,
thur,
fri,
sat,
sun
};
2、枚举中的成员也被称为枚举常量,枚举常量在默认未初始的情况下都是从0开始依次递增1。也可以给枚举常量赋一个初始值,后面的成员也是依次递增1。
3.3枚举的优点
1、增加代码的可读性和可维护性
2、和#define定义的标识符比较枚举有类型检查,更加严谨,#define定义的表示在编译的过程中就会替换,无法在调试时更精确的监视
3、防止命名污染(封装)
4、便于调试
5、使用方便,一次可以定义多个常量
4.1联合类型的定义
1、联合是一种特殊的自定义类型,联合体关键字union
union Un//Un:联合体名
{
char a;
int b;
double c;
};
4.2联合体的特点
1、联合体的成员是共用同一块空间,联合体的大小至少是最大成员的大小
2、联合体因为是共用同一块空间,使用的时候一次只能访问初始化的时候一次只能初始化一个成员,其他成员没有初始化时也可以访问到其中一个初始化的值,根据下面样例就可以得出联合体是共用同一块空间
union Un u={'a',5,4.5}//该初始化是错误的,编译的时候会直接报错
3、联合体的大小计算
联合体的大小至少是最大成员的大小,当联合体成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数
以上就是我对结构体、枚举、联合三种自定义类型的学习总结,若有错误或有补充的知识点欢迎大家在评论区指出,我将会马上修改。欢迎大家一起来学习编程、分享编程知识!!!