1.指针:
(1) 概念:地址(内存编号,本质就是个数字),是一种雷系,是一个变量。
(2) 类型:指针变量的类型是指针类型;指针指向的空间中的数据类型。
ps:指针指向空间的类型决定了能够访问多大的空间以及对空间中的数据如何解释。
(3) 运算:只能进行加减运算,指针变量保存的是一个地址(数字),因此指针的加减就是指向的偏移,但是偏移不是以字节为单位,而是以指向类型大小为单位。
(4) 不同类型的指针:
基础类型指针:char*,int *,long*,float*,double*,void*
char*:字符指针,通常也可以指向一个字符串空间的首字符位置,模拟字符串操作,从首字符开始连续访问知直到\0’。
void*:无指向类型的指针,因此无法解引用,通常作为通用指针使用。
(5)指针数组:一个数组,元素都是指针,eg:int *p[20];
数组指针:一个指针,指向的是数组,eg:int (*p)[20];
数组名:通常表示数组首元素的地址,指针步长是首元素大小。
&数组名:数组的空间起始地址,与首元素地址其实一样,但指向类型不同,&数组名指向的是数组,指针偏移步长是数组。
函数指针:一个指针,指向了一个函数,eg:int (*p)(int a,int b);
回调函数:定义一个函数后,并不直接调用,而是把函数当作参数传递给其它函数,在合适的时候被调用。
函数指针数组:一个数组,每个元素都是函数指针,eg:int(*arr[10])(int a,int b);
2.自定义类型:结构体;联合体;枚举
结构体类型:是将多个基础数据类型结合到一起所定义的一个复杂数据类型。(基础数据类型:char,short,int,long,float,double)
定义结构体类型:eg:struct dog_t { int size; int weight; char name[32]; char voice[32]; };
自引用:struct student_t {int sn; char name[32]; struct student_t *pnstu} ;
结构体变量的定义及初始化:struct student_t stu ={1,"张三”,NULL}; 或 Stu stu2;
PS:还可以如此定义:struct student_t {int sn; char name[32]; struct student_t *pnstu} stu1;
PS: struct student_t {int sn; char name[32]; struct student_t *pnstu} stu1;
typedf struct student_t {int sn; char name[32]; struct student_t *pnstu} stu;
--> stu1是定义的一个结构体变量,stu时一个类型
但是当要描述一个复杂对象时,用基础数据类型就比较麻烦(比如某一个单体),此时就需要一种能简单完整描述一个复杂对象的数据类型(比如单体类型)。
结构体类型其实就是将多个基础数据类型合并到一起,组成一个整体的复杂数据类型。
结构体变量中的成员变量,只有在初始化时可通过赋值进行初始化,定义完后就不能使用赋值进行整体的成员变量初始化。
PS:链表式
3.结构体的内存对齐:让结构体的每个成员变量在内存中存储时对齐到指定的位置。
当CPU从内存中取数据时,数据总线位数时32位,则一次可以从内存中去除4字节数据。但若四字节数据从一个存储颗粒中取出,则是串行的。为了提高效率多加几颗存储颗粒,从每个存储颗粒中取出一个字节,就可以并行同时取出4字节数据。因此有些硬件平台CPU只能从指定地址处取出指定大小的数据。
结构体的内存对齐的原因:
1.不是所有的硬件平台都支持随意地址访问,只能从指定位置开始访问指定大小的数据;
2.便于cpu对数据的访问,可以提高结构体成员的访问效率。
内存对齐规则:
1.在任何一个平台下都有一个默认对齐数,VS中默认对齐数为8。
2.结构体每个成员变量又有一个自己独立的对齐数(即成员变量的类型大小),取较小的作为实际对齐数。
3.实际成员变量的对齐数是成员自己独立的对齐数与默认对齐数中较小的那个。
4.每个成员变量都应对齐到这个对齐数的整数倍。
5.结构体的总大小应该是所有成员变量中实际最大对齐数的整数倍,不足则补位。
6.在结构体嵌套定义中,若结构体的成员也是一个结构体变量,则这个结构体变量的对齐数是自己内部成员的最大对齐数。
eg:
PS: 默认对齐数的修改:
#pragma pack(n) //将默认对齐数设置为n
#pragma pack //还原默认对齐数
为什么要修改默认对齐数:
涉及到网络通信,将一个结构体变量(一块连续的内存数据)发送给了另一个主机,若两个平台对齐数不同,在进行内存中数据解释时就会出现问题,因此两个主机都必须使用指定大小的对齐数。
4.结构体位段:一个结构体类型中可以有多个基础数据类型的成员。即让结构体中的多个成员变量可以使用同一块空间中的不同比特位进行数据存储,以此节省使用空间。
eg :
struct tem_t{
char a:3; char b:5;
}; //该结构体大小是一个字节,a成员从地位开始占了3个比特位,b成员从地位开始的第四个比特位开始占据5个比特位。
eg:
struct xuesheng_t {
int sn;
char sex:1;//0男,1女 //表示占一个字节里的一个比特位
char pos:2;//0普通,1班长,2学委,3劳委 //表示占一个字节里的两个比特位
int score;
};
此时的结构体大小为12个字节,此时占用的空间就太多了。性别只有0或1,1个比特位其实够;
职位只有0、1、2、3,两个比特位就够。此时,使用位段,memset:n(表示使用指定空间中n个比特位存储数据,且多个位段可以合并占用同一个存储空间。)
如sex与pos成员,共同使用一个字节空间就够了,因此会合并使用一个字节,在vs平台下从地位开始赋值。
PS: 多个位段使用同一块内存空间,从低位开始向高位存储数据,若比特位不够则申请一个新的空间存储。
若第一个位段剩余空间不足存储第二个位段,则会为第二个位段新开辟空间存储。
位段进行数据存储时,若数据过大则会产生截断,如3个比特位存储10-1010,则会截取010进行存储。
与结构相比,位段可以达到同样效果,但可以很好的节省空间,但有跨平台的问题存在。
位段的类型只能用整型,不能用浮点型。
位段的跨平台问题:
1.int位段被当成有无符号数时不确定的;
2.位段中最大位的数目不能确定(16位机器最大16,32位机器最大32,写成27,在16 位机器会出现问题)
3.位段中 的成员在内存中从左向右还是从右向左分配标准尚未定义;
4.当一个结构包含两个位段时,第二个位段成语较大,无法容纳于第一个位段剩余的位时,时舍弃剩余的位还是利用,是不确定的。