指针的定义:
指针是一种数据类型(无符号整数,这种整数代表了内存的编号),使用它可以定义的变量叫指针变量(简称指针,这种变量战用4|8字节内存大小)。
为什么使用指针?
1、函数之间共享变量:
全局变量可能会名字冲突不可过多使用。
函数之间传参是值传递的,因此不能共享变量。
函数之间的命名空间是独立的,但是存储空间是统一,可以把存储的地址在函数之间传递,达到共享的目的。
2、堆内存无法取名字(标识符无法与内存建立映射关系),只能与指针配合使用。
3、传递函数之间的传参是值传递(内存拷贝),当要传递的变量大于4字节时,可以把变量的地址传递过去(只需要拷贝4字节,因为变量的地址是个整数)。
注意:指针是一种非常灵活、功能十分强大的能够直接操作内存的方式,但也是十分危险,不该用的时候不要乱用。
如何使用指针:
定义指针变量:类型* 指针变量名;
short* p = 10;
1、指针变量的用法与普通变量不同,为了区分它一般以p结尾。
2、指针变量不能连续定义,一个只能定义出一个指针变量。
short p1,p2,p3; // error p1是指针,p2,p3是short变量
short *p1,*p2,*p3; // p1,p2,p3都是指针变量
3、指针的类型决定了通过指针访问内存读取几个字节,int类型读取4字节,double类型读取8字节,char类型读取1字节。
4、指针变量的默认值是随机的,为了安全一定要初始化,一般初始化为NULL(空指针)。
赋值:
p = malloc(2); // 从堆内存分配两个字节,并把首地址赋值给p
short num = 10;
p = # // 计算出变量的地址,赋值给指针变量,赋值的栈空间地址。
取值:根据指针变量中记录的地址编号去访问内存(解引用),但这个步骤可能会出现段错误(这是因此之前对指针变量赋值的是一个非法的内存地址)。
*指针变量 <=> 变量;
*p <=> num;
使用指针要注意的问题
空指针:
在C语言一旦空指针进行解引用就会出现段错误。
在大多数系统下NULL就是0地址,由于操作系统把这个地址当作了复位地址(当操作系统重启时,会跳转到这个地址执行,所以它里面存储的是操作的用来重启的数据),所以操作系统禁止访问这块内存,一旦解引用就会出现段错误。
如何杜绝空指针造成的段错误:使用来历不明的指针前要先判断是否是空指针。
if(NULL == p)
NULL不一定是0,有些系统下也有可能是1。
野指针:
指向的内存不明确,这种指针叫野指针(野指针不代表就是错的,也有可能是正确的)。
使用野指针不一定会出错,但这也正是它危险的地方,因为无法证明,也无法判定,而且危险是潜在的。
使用野指针的后果:
1、一切正常
2、段错误
3、脏数据
如何避免使用野指针:
由于无法判断是否是野指针,因此只能从源着手,不制造野指针是每个程序员应尽的职责。
如何避免产生野指针:
1、定义指针时初始化。
2、不获取局部变量的地址(在函数执行完后局部变量就会被释放)。
3、指针指向的资源被释放后,指针要及时赋值为空。
指针的运算:指针变量里存储的就是整数,所以整型变量能够使用的运算符,指针变量都能使用,但对指针变量的所有运算不是都有意思的。
指针 + 整数 <=> 指针+整数指向的类型的宽度
指针变量向右移
指针 - 整数 <=> 指针-整数指向的类型的宽度
指针变量向左移
指针 - 指针 <=> (指针-指针)/指向的类型的宽度
计算两个指针变量之间相隔多少个元素。
指针 <|> 指针 可以计算出指针谁在左谁在右。
%p 是以十六进制方式显示指针变量的值。
指针与数组
数组名就是个指针,是一种特殊指针(常指针),不能修改。
数组名与数组的首地址是对应关系,而指针是指向关系。
数组名可以使用指针的语法,指针也可以使用数组的语法。
arr[i] <=>*(arr+i)
当数组作函数的参数时,它就蜕变成了指针,在函数中无法计算出数组的长度,因此需要再添加一个参数把数组的长度传递过来。
指针与const配合
const int* p; // 保护的是指针指向的内存,不能通过指针去修改内存的值。
int const p; // 同上
int const p; // 保护的是指针变量,不能修改指针变量的值。
const int* const p; // 既保护的是指针变量又保护指针变量指向的内存。
int const* const p; // 同上