一、指针
1.指针是什么?
指针是一种变量,用来保存数据(即地址)
保存的数据固定长度(也就是地址的长度固定,由操作系统决定。
占用字节4/8个
2.指针运算
- :取值运算符
*ptr :取指针ptr变量对应对应内存空间的地址所对应的内存空间的值
取ptr变量指向的内存的值
对应内存空间 VS指向的内存空间
对应内存空间:即储存变量ptr的的地方
指向的内存空间:变量ptr保存的是地址,这个地址指向的空间就是指向的内存空间
多级指针的作用
保存上一级指针变量的地址
指针数值运算
ptr++:指针向后走一个字节
ptr + 1:指针向后走一个步长
指针的步长:由指针的类型决定
指针赋值运算:
必须是步长相同的指针,才能进行赋值运算
void :空指针,即万能指针
1.可以接受任意类型指针的赋值
2.不能做取值操作
3.步长是一个字节
4.赋值是,void 必须进行强制类型转换,转换到确定的类型 // int *ptr1 = (int *)void *ptr2;
两个指针之间,只能相减(计算两个指针之间相差的位数),不能相加
3.野指针
由于编译器优化,所有的未指明地址的指针会被初始化为0,即将指针置为NULL。零地址是不能操作、不能赋值的,一般编译时会报段错误,而且这种错误不容易检查。
所以,我们在定义指针时,对于没有初始地址的指针,要给它赋值为NULL,提醒自己该指针是不能操作、不能赋值的,避免出现错误。
int main()
{
char *ptr; <------ 野指针,会随机指向一个地址,导致内存泄漏
}
什么是野指针?
随机指向一块内存的指针;释放空间后的指针;
野指针的影响?
会导致内存泄漏
如何避免野指针?
要养成良好的编码习惯:
1.指针没有指向时,要置空;char* ptr = NULL;
2.指针被释放后,要置空。
给指针分配空间、并释放
char* ptr = (char *)malloc (sizeof(char)*100);
free(ptr);
ptr = NULL;
指针被置空的原因?
提示自己该指针不能操作、不能赋值,防止出现错误。
4.malloc 分配内存空间 单位:字符
mallloc的返回值是 void *
形参、传参:
char *ptr = (char *)malloc(sizeof(char) * 100)
malloc的返回值要进行强制类型转换
检查mallloc是否分配成功
char *ptr = (char *)malloc(sizeof(char) * 100)
if(NULL == ptr)
{
printf(“malloc is error\n”);
exit(1);
}
对分配的空间进行初始化
meset(ptr, 0, sizeof(char) * 100)
ezero(ptr, sizeof(char) * 100)
使用时不要越界访问
------>free的注意事项
释放之后要将指针置空
free(ptr);
------>free是如何知道要释放多大的内存?
malbc
需要额外40个字节
来保存malbc分配的100个字节的信息、属性
…
…
存储100个字节的信息
…
…
…
…
malbc的缺点:大开销操作(无论存储多少个字节的信息,都需要40个字节来保存这些字节的信息、属性)
------->解决方法:内存池
------->malbc是如何实现的?malbc是如何获取内存的?
二、数组
1.一维数组 //int a[100]
a 数组名:是一个指针常量(不能用来自加自减),保存数组首元素的地址
&a &数组名:数组的地址
int num = 5;
num 0x1003
0x1002
0x1001
0x1000
一个变量的地址由首个字节的地址决定&num = 0x1000
二、数组
1.一维数组 //int a[100]
a 数组名:是一个指针常量(不能用来自加自减),保存数组首元素的地址
&a &数组名:数组的地址
int num = 5;
num 0x1003
0x1002
0x1001
0x1000
一个变量的地址由首个字节的地址决定&num = 0x1000
int a[4] = {0};
a[3] 0x100c
a[2] 0x1008
a[1] 0x1004
a[0] 0x1000
&a—>0x1000 a---->0x1000
2.数组指针
实质上是一个指针,用来保存一个数组的地址
int (*pa)[3]
pa = &a; *pa = a;
—>对一组数组的地址取值,等于首元素的地址
3.二维数组
二位数组的输入输出
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
int aa[2][2] = {0};
for(i = 0;i < 2; i++)
{
for(j = 0;j < 2; j++)
{
scanf("%d", &a[i][j]);
//printf("%d",a[i][j]);
}
}
retuen 0;
}
aa 二维数组名:是一个指针常量,保存的是二维数组中首个以为数组的地址
&aa &二维数组名:取二维数组的地址
4.二维数组指针
int (*paa)[3];
paa = &aa;
5.三维数组
int aaa[2][2][2] = {{1, 2, 3, 4},{5, 6, 7, 8} //有2个二维数组
aaa[0][0][0] = 1; aaa[0][1][0] = 3;
aaa[1][0][0] = 5; aaa[1][1][0] = 7;
aaa 三维数组名:是一个指针常量,保存的是三维数组中首个二维数组的地址
*aaa :三维数组中首个二维数组中的首个一维数组的地址
**aaa:三维数组中首个二维数组中的首个一维数组的首元素的地址。
6.三维数组指针
int (*paaa)[3];
paaa = &aaa;
7指针数组:实质上是一个数组,数组里面放的是指针
int *ptr[3];
指针数组里的元素一开始都是野指针,要对其进行分配空间
-------->指针数组主要用来给主函数传参:
argc:统计传入主函数参数的个数(其中包括编译命令)
*argv[]:传入的参数保存在argv中
使用:
int main(argc,char *argv[]//或者:char **argv)
入口输入检查(检查输入的变量是否符合要求)
if(argc != 3)
{
exit(1);
}
8.函数指针:保存函数的入口地址(即函数的地址)
int (*p_func)(int, int);
函数名与&函数名:意义相同,表示一个指针,保存的是函数的口地址
使用:
int (p_func)(int, int);
p_func = add;
函数指针作为形参指向的函数,称为回调函数
9.函数指针数组
是一个数组,数组内的每一个元素都是函数指针
int ( p_arry[4])()