1.指针到底是什么
指针的实质就是个变量,它跟普通变量没有任何本质区别。指针完整的名字应该叫指针变量,简称为指针。
代码:
// a的实质其实就是一个编译器中的符号,在编译器中a和一个内存空间联系起来
// 这个内存空间就是a所代表的那个变量。
int a; // 定义了int型变量,名字叫a
int *p; // 定义了一个指针变量,名字叫p,p指向一个int型变量
a = 4; // 可以操作
p = 4; // 编译器不允许,因为指针变量虽然实质上也是普通变量,但是它的
// 用途和普通变量不同。指针变量存储的应该是另外一个变量的地址
// 而不是用来随意存一些int类型的数。
p = (int *)4; // 我们明知道其实就是数字4,但是我强制类型转换成int *类型的4
// 相当于我告诉编译器,这个4其实是个地址(而且是个int类型变量
// 的地址),那么(int *)4就和p类型相匹配了,编译器就过了。
2.为什么需要指针?
指针的出现是为了实现间接访问,间接访问(CPU的间接寻址)是CPU设计时决定的,这个决定了汇编语言必须能够实现间接寻址,又决定了汇编之上的C语言也必须实现简介寻址。
3.指针使用三部曲:定义指针变量、关联指针变量、解引用
// 演示指针的标准使用方式
// 指针使用分3步:定义指针变量、给指针变量赋值(绑定指针)、解引用
int a = 23;
// 第一步,定义指针变量
int *p;
printf("p = %p.\n", p); // %p打印指针和%x打印指针,打印出的值是一样的
printf("p = 0x%x.\n", p);
// 第二步,绑定指针,其实就是给指针变量赋值,也就是让这个指针指向另外一个变量
// 当我们没有绑定指针变量之前,这个指针不能被解引用。
p = &a; // 实现指针绑定,让p指向变量a
p = (int *)4; // 实现指针绑定,让p指向内存地址为4的那个变量
// 第三步,解引用。
// 如果没有绑定指针到某个变量就去解引用,几乎一定会出错。
*p = 555; // 把555放入p指向的变量中
(1)当我们int *p定义一个指针变量p时,因为p是局部变量,所以也遵循C语言局部变量的一般规律(定义局部变量并且未初始化,则值是随机的),所以此时p变量中存储的是一个随机的数字。
(2)此时如果我们解引用p,则相当于我们访问了这个随机数字为地址的内存空间。那这个空间到底能不能访问不知道(也许行也许不行),所以如果直接定义指针变量未绑定有效地址就去解引用就有可能会出现错误。
(3)定义一个指针变量,不能不经绑定有效地址就去解引用。
(4)指针绑定的意义就在于:让指针指向一个可以访问、应该访问的地方,指针的解引用是为了间接访问目标变量。
4.什么是野指针?
(1)野指针,就是指针指向随机的、不正确的、没有明确限制的的位置。
(2)指针变量在定义时如果未初始化,值也是随机的,很可能触发运行时段错误。
(3)野指针因为指向地址是不可预知的,所以有3种情况:
第一种:是指向不可访问,操作系统不允许访问的敏感地址,譬如内核空间的地址,结果是触发段错误。
第二种是指向一个可用的、而且没什么特别意义的空间(譬如我们曾经使用过但是已经不用的栈空间或堆空间),这时候程序运行不会出错,也不会对当前程序造成损害。
第三种情况就是指向了一个可用的空间,而且这个空间其实在程序中正在被使用(譬如说是程序的一个变量x),那么野指针的解引用就会刚好修改这个变量x的值,导致这个变量莫名其妙的被改变,程序出现离奇的错误,这种危害是最大的。
5.怎么避免野指针?
(1)野指针的错误来源是定义了没有初始化,也没有赋值,然后去解引用。所以,在指针的解引用之前,一定确保指针指向一个绝对可用的空间。
->定义指针时,同时初始化为NULL
->在指针解引用之前,先去判断这个指针是不是NULL
->指针使用完之后,将其赋值为NULL
->在指针使用之前,将其赋值绑定给一个可用地址空间
int b,a=5;
int *p = NULL;
p = &a; // 正确的使用指针的方式,是解引用指针前跟一个绝对可用的地址绑定
if (NULL != p)
{
b=*p ;
}
p = NULL; // 使用完指针变量后,记得将其重新赋值为NULL
(2)NULL的实质其实就是0,然后我们给指针赋初值为NULL,其实就是让指针指向0地址处,这个0地址在一般的操作系统中都是不可被访问的。