指针
什么是指针:
指针是一种特殊的数据类型,使用它可以定义指针变量,指针变量中储存的是整型数据,代表了内存的编号
通过这个编号可以访问到对应的内存。
为什么使用指针:
1、函数与函数之间是相互独立的,参数无法进行传递,又想要共享变量时
传参是单项值传递
使用数组还需要传递数组长度
全局变量,但是不推荐容易和第三方库冲突,个数不宜多,当多个函数都用使用时还是可以使用。
函数命名空间是相互独立的,但是地址空间是同一个地方,所以指针可以解决这个问题
2、因为函数传参是值传递(内存拷贝),当字节过多时效率过低,如果传递的是变量的地址,只需要
传递4|8个字节取决于计算机位数。
3、堆内存取名它不像stack、bss、data、让变量名与内存建议关系,只能使用指针来记录堆内存的地址编号
从而使用该堆内存
如何使用指针:
定义:
类型* 变量名_p; int* p;
注意:
1、指针变量与普通变量的用法上有很大的区别,为了以示区分,建议在变量名后面加上 _p 。
2、指针的类型决定了存储的是什么类型变量的地址,指针的类型决定了可以通过指针变量访问的字节数。
3、一个* 只能定义一个指针变量
int a1,a2,a3;
int* p1,p2,p3;//p1是指针,p2,p3是int
int *p1,*p2,*p3;//这样一来 p1,p2,p3都是指针变量
4、指针变量与普通变量一样的是默认值是随机的,所以一般都要初始化NULL,代表空指针
赋值(引用):
变量名_p = 地址;地址必须是一个有权限而且有意义的内存地址,如果给错了不会报错!
指向栈内存:
int num;
int* p = #
or
int num;
int* p = NULL;
p = #
指向堆内存:
int *p = NULL;
p = malloc(4);
解引用:
*p 通过指针变量中的地址记录的内存编号来访问对应的内存,该过程会产生段错误,原因是存储了非法的内存编号。
注意:访问的字节是由指针变量的类型决定的
*p = 1000 ;但是上诉如果给地址时错误了,这里给值就会报错
练习1:实现一个变量交换的函数,调用它对一个数组进行排序。
#include<stdio.h>
void swap(int* a,int* b)
{
int num=0;
num = *a;
*a = *b;
*b = num;
}
void show(int a[],int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",a[i]);
}
}
int main(int argc,const char* argv[])
{
int a[5]={0,2,1,3,4};
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
if(a[j]>a[j+1])
{
swap(&a[j],&a[j+1]);
}
}
}
show(a,5);
return 0;
}
注意:想要获取多个返回值,可以借助指针返回。
练习二:实现一个函数,计算两个整数的最大公约数(用return)、最小公倍数(指针返回)
使用指针需要注意的问题:
空指针:
值是NULL的指针都是空指针、如果对空指针进行解引用立即产生段错误。
NULL也是一种错误标志、如果一个函数返回值是指针类型时,当函数执行出错可返回NULL表示函数执行出错。
NULL在绝大多数系统中都是0,在个别系统中是1。
if(NULL == p)
if(!p)
如何避免空指针带来的段错误:
1、当函数的参数是指针时,别人传来的可能是空指针
2、从函数获取返回值时,也可能获取到的空指针
野指针:
指针的指向不确定的内存。
野指针解引用的危害:
1、会产生段错误
2、脏数据
3、一切正常
野指针的危害比空指针更严重、因为它无法判断出来,而且就算一切正常再一次运行还是有可能不通过,隐藏错误
短时间不会报错
避免野指针:
1、定义指针时一定要初始化
2、函数不要返回局部变量的地址!!!!!!!!!!!!!!!!!!
3、指针指向的内存被释放后、指针变量立即置空 = NULL;
4、在接受到指针时及时判断
指针的运算:
指针变量里储存的是整形,理论上整型数据能使用的运算符号他都能用。但是绝大部分都是无意义的
指针+n: 指针+指针类型的宽度*n 前进了n个元素
指针-n: 指针- 指针类型的宽度*n 后退了n个元素
指针-指针:指针和指针之前相隔了多少个此类型的宽度或者说有多少个元素 (指针-指针)/ 类型宽度
指针与const
const int*p; 保护指针指向的内存不被修改
int const *p;同上
int* const p;保护指针变量不会修改
const int* const p;保护内存和指针变量不会修改
int const* const p;同上
这些类型限定符始终都遵循着---就近原则---
为了提高传参效率而使用指针时,传参的效率虽然提高了,但是变量也有被修改的风险。换句话说你把你家钥匙给了别人 谁都能进
这种写法就可以保护指针变量所指向的值不被修改,钥匙给了但是进不去哈哈
小结分析
int swap(int* a,int* b)
{
int c;
if(*a<*b)//这里比较的是两个值的大小!
{
c = *a;
*a = *b;
*b = c;
}
}
与
int swap(int* a,int* b)
{
int c;
if(a<b)//这里表示的是两个值的储存位置门牌号,按照系统的规则这是一个永远成立的条件,因为b定义在了a后面所以永远b>a
{
c = *a;
*a = *b;
*b = c;
}