一.什么是指针
理解指针就要从机器级开始了解.
我们知道在机器中,数据内存中是以一个个字节进行存储的,比如我要创建一个int类型的a变量,并赋值10,那么进行的操作就是申请4个字节的空间,然后在相应的空间将10用二进制表示为补码存进空间里面.
如下图所示:
那么我们会发现每一个字节都有一个对应的地址,这个地址就被称为指针.
程序中的变量可能占一个字节或多个字节内存,但我们只把第一个字节的地址作为该变量的地址.
类似于酒店中住着一个个旅客(一个个不同类型的数据),而他们所占房间的门牌号就是一个个地址.
用来存放地址的变量,我们称为指针变量.(存放在指针变量里面的数据都会被默认为地址)
在用指针变量p存储变量i的地址时,我们会说p指向i.换句话说,指针实际上就是地址,而指针变量负责存储地址(指针).
补充:
在大多数情况下,指针和地址是完全相同的
二.指针变量的大小
假设一个字,占36个比特位,一个字对应一个地址,对于整数来说,一般4个字节,也就是32位就可以表示,现在有36位,因此现在地址(指针)确实就是指向一个整型.
但是我们知道一个字符,只需要一个字节,8位就可以表示,那一个字中,其实可以表示多个字符,现在问题便出来了,我们这个地址(指针),指向的是哪个字符呢?
而在这种情况下,指针并不和地址相同,而是表示偏移量,CPU需要把它和存储在专用寄存器中的段值结合起来.
三.取地址运算符和间接寻址操作符
为了使用指针,C语言开发了两种运算符
第一种是取地址运算符&,顾名思义,&i表示取出变量i的地址
第二种是间接寻址操作符*,假设我们将变量i的地址存到变量p中,通过*p,我们便可以找到i,类似于,知道房间门牌号,便可以相应访问对应房间号的数据.
四.指针变量的声明
指针变量的申明和普通变量声明类似,不过要在前面加上一颗*
值得注意的是,C语言要求指针只能指向一种特定类型(引用类型)的对象
比如:
double *p = NULL; //指向double类型的指针
int *p1 = NULL; //指向int类型的指针
float *p2 = NULL; //指向float类型的指针
p指向double类型数据后,就不可能指向int类型,并且也不可能指向多个数据,存储的只是一种引用对象的地址.当然引用对象没有限制,甚至可以指向指针,数组,结构体等等都是可以的.
PS:
指针的类型,决定了指针移动步长和解引用时访问的空间.
如图所示,pc,pi指向的都是n的地址,但是pc+-步长与pi+-步长所移动,得到的地址时不一样的,pc为char*类型,移动的仅仅为1字节,而pi类型为int *,移动的则为4个字节.
*pc访问的只是一个字节的内容,因此*pc = 0,仅仅将低位的44改成了00.
*pi为int*类型,解引用访问的便为4个字节的空间因此全部都会赋值为0.
五.指针赋值
1.指针初始化
结合上述两个操作符,我们就可以对指针变量p赋值,变量a的地址便可以存进p中.
*p就可以访问a的空间,因此,a的值我们可以发现,原来是11,后面又被重新改回10l了.
PS:C语言是允许具有相同类型的指针进行赋值的.
比如
看上面的程序,p,q都是指针,p存储变量i的地址后,可以赋值给q,此时q同样指向变量i.
这里也可以看出,C语言中任意数量的指针可以指向同一个变量.
而*q=*p,则是把p指向的变量i的值赋给q指向的变量j,需要注意,两个式子是不同的
2.野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针出现的情况一般有三种
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
假如指针未进行初始化,则p指向随机的地址,如果改变的内存单元属于操作系统,那么很可能会导致系统的崩溃.造成的后果是无法想象的!!!
Lg2.
int* p = &i, i;
这段代码同样也是犯了这个错误,i变量还没有创立,p指针无法指向i,根本就没有初始化,则编译器必然会报错.
Lg3.
出了函数外部,局部变量i就会自动销毁,此时假如用一个指针变量p,接受&i,也是毫无意义的.
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0; }
3.如何规避野指针
#include <stdio.h>
int main()
{
int *p = NULL;//指针及时初始化,无地址可赋值为NULL先
//....
int a = 10;
p = &a;
if(p != NULL)//指针使用前,判断是否位NULL
{
*p = 20;
}
return 0;
}