指针
什么是指针:
指针是一种数据变量(例如int,char),使用它可以定义指针变量,这种变量用来存储内存地址,使用指针变量可以访问对应的内存,具体访问多少
个字节由指针的类型所决定。
什么情况下使用指针
理论上我们使用指针可以访问任何位置的内存,但绝大部分内存我们是没有访问权限的,因此使用指针是容易产生错误(原因就是非法访问内存),
所以我们要在合适的时候使用指针,例如以下情况
1、函数之间共享局部变量
函数之间共享局部变量可以使用全局变量,但是全局变量容易造成命名冲突,还容易浪费内存,故在需要大量共享局部变量时不适用
而函数传递默认是单向值传递,无法共享变量,所以指针时函数间共享变量的最好选择
2、提高函数传递参数的效率
函数传参默认是单向值传递(内存拷贝),当传递变量的字节数较多时(如结构体),传递变量的地址效率更高,32位系统下只需要4字节,64位系统下只需要8字节。
但这样会带来一个风险:变量可能会被修改。 可以使用const配合指针保护变量。
3、配合堆内存使用
堆内存无法取名(不能与标识符建立映射关系),因此必须与指针配合使用。
如何使用指针
定义指针变量: 类型* 指针变量_p;
如 int* p = NULL;
1、指针变量不能连续定义。
int* p1,p2; p1是指针,p2是int类型
int *p1,*p2; p1,p2都是指针
2、指针变量与普通变量一样默认值都是随机的(野指针),所以使用指针时一定要初始化。
3、指针的类型决定了访问内存的字节数 ,如int* 访问4个字节的内存。
指针变量的赋值与解引用
int num = 10;
int* p1 = #
int* p2 = malloc(sizeof(int));
printf("%d\n",*p1);
使用指针需要注意的问题
空指针:
指针变量值为NULL的指针叫做空指针,这种指针不能解引用,否则会产生段错误。
使用指针时需要对空指针进行判断
if(0 == NULL)
{
printf("该指针为空指针\n");
}
野指针:
指向不确定的内存的指针
对野指针解引用的后果:
1、段错误
2、脏数据
3、一切正常
野指针比空指针危害更大,因为它是无法判断出来的,错误可能是隐藏的,短时间不会显现
如何避免野指针:
1、定义指针时一定要进行初始化
2、函数不返回局部变量地址
3、指针变量指向的内存被释放后,指针变量要及时置空(=NULL)
指针数组与数组指针
指针数组:
是由指针变量组成的数组,它的成员都是指针变量(本质上是数组)
类型* arr[长度];
例如 int* arr[10];
数组指针:
是一种指针,专门用来存储数组的地址。
类型 (*arrp)[长度];
int* arr[10];
printf("%d\n",sizeof(arr));
int (*arr)[10];
printf("%d\n",sizeof(arr));
指针的运算:
指针变量中存储的是整数,理论上整数支持的所有运算指针变量都支持,但是绝大多数的运算是无意义
指针 + n:指针+指针类型字节数*n 前进了n个元素
指针 - n:指针-指针类型字节数*n 后退了n个元素
指针 - 指针:(指针-指针)/指针类型字节数 计算出两个指针之间间隔了多少个元素
注意:只有类型相同的指针才能相减。
const与指针:
当我们为了提高传参效率而使用指针时,传参的效率提高了,但是变量也有被修改的风险,这种情况可以使用const来进行保护
const int* p; 保护指针所指向的内存不被修改
int const* p; 同上
int* const p; 保护指针的指向不能修改
const int* const p; 保护指针指向的内存和指向都不能修改
int const* const p; 同上
数组名与指针:
数组名其实是一种特殊的指针,但它是常量,不能修改它的值,它与内存之间是映射关系,不能改变,而指针与内存之间是指向关系,可以改变
与普通指针的区别:
1:数组名是常量,普通指针是变量
2、普通指针变量有自己的存储空间,用来存储内存编号,它与目标内存是指向关系
3、数组名没有自己的存储空间,他就代表数组空间的首地址,他与数组内存是映射关系
4、对数组名取地址结果还是数组名的值
int arr[5];
arr<=>&arr
arr 类型 int*
&arr 类型 int(*)[n]
数组名是没有存储空间,而指针变量是有存储空间
指向数组的指针,可以当做数组名使用
数组名也可以当做指针使用
数组名[i] == *(数组名+i)
* (指针名+i) == 指针名[i]
注意:当数组作为参数传递时变成了指针,所以长度丢失了
二级指针:
二级指针就是指向指针的指针,里面存储的是指针变量的地址。
定义:
类型** 变量名_pp;
int num;
int* p=#
int** pp=&p;
赋值:
变量名_pp = &指针变量;
解引用:
*pp <=> p
**pp <=> *p<=>num
函数指针:
函数会被二进制指令存储到代码段,而函数名就是指令的首地址
函数名就是地址(整数),它代表了函数在代码段中的位置
函数指针就是指向函数的指针,里面存储的是函数在代码段中的位置
如何定义函数指针:
1、复制函数的声明语句,
2、给函数名加上小括号,并在函数名前加一个*
3、给函数名重新取一个函数指针变量名
当我们把函数作为数据传递时,被调用函数,可以通过函数指针调用我们以参数形式提供的函数,这种模式叫回调
比如我们标准库中的qsort和bsearch。
由于函数指针的类型比较长,一般会选择使用typedef进行类型重定义
void func(int,int);
typedef void (*FP)(int,int);
void指针:
1、以一字节为单位移动
2、不能解引用
3、它可以与任何类型的指针自动转换(只能在c语言中)
4、一般用作函数的参数、返回值