目录
一、什么是指针
指针是内存中一个最小单元的编号,也就是地址。 平时口语中所说的指针是指针变量,用来存放内存地址的变量。
总结:指针就是地址,口语中的指针是指针变量,指针变量是用来存放地址的。指针的大小在32位平台上是4个字节,在64位平台上是8个字节。
int main()
{
int a=19;
int* pa=&a;
*pa=20;
printf("%d\n",a);
}
打印结果为:20
a是一个变量,里面假设存储了数字19,则,&a获取了a的首地址,即位006FF9F8,pa是指针变量,它用来存放&a存放的地址006FF9F8。
代码中:int* pa=&a,&a意思是取a的地址,pa存储了a的地址,而int*的意思是,*表示pa是指针,int表示pa指针所指的对象a的类型是int型。
二、指针和指针类型
1.1 指针的解引用
int main()
{
int a=0x11223344;
//a是十六进制位数字
char* pa=(char*)&a;
*pa=0;
}
a是整型,但指针变量却用了char类型,(char*)强制转换,也可实现运行。
但是在解引用*pa=0;时,因为是char类型所以,只访问一个字节。
结论:指针类型决定了指针在被解引用的时候访问几个字节。
如果是int*类型的指针,解引用访问4个字节
如果是char*类型的指针,解引用访问1个字节
如果是double*类型的指针,解引用访问8个字节。
1.2 指针+-整数
int main()
{
int a=0X11223344;
int* pa=&a;
char* pc=&a;
printf("pa=%p\n",pa);
printf("pa+1=%\n",pa+1);
printf("pc=%p\n",pc);
printf("pc+1=%p\n",pc+1);
return 0;
}
打印结果:pa=006FF9F8
pa+1=006FF9FC
pc=006FF9F8
pc+1=006FF9F9
pa到pa+1:增加了4个字节
pc到pc+1:增加了1个字节
结论:指针的类型决定了指针+-1操作的时候跳过了几个字节,即决定了指针的步长。
三、野指针
野指针就是指针指向位置是不可知的(随机的,不正确的,没有明确限制的)
3.1 野指针成因
1.指针未初始化
int main()
{
int* p;//p没有初始化,就意味着没有明确的指向,一个局部变量不初始化的化,放的就是随机值
*p=10;//非法访问内存,这里的p就是野指针
return 0;
}
2.指针越界访问
int main()
{
int arr[10]={0};
int* p=arr;//arr指的是数组首个元素的地址,数组名为数组首元素地址
int i=0;
for(i=0;i<=10;i++)
{
*p=i;
p++;
}
return 0;
}
3.指针指向的空间释放
int* test()
{
int a=10;
return &a;
}
int main()
{
int* p=test();
return 0;
}
test()函数运行时,获取了a的地址,主函数获取了a的地址,但此时test()函数的空间已经释放,主函数获取a的地址,通过a的地址找到a,但是由于已经释放,无法找到a。
3.2 避免野指针一般出现
1.指针初始化
2.小心指针越界
3.指针指向空间释放,即使置NULL
4.避免返回局部变量地址
5.指针使用之前检查有效性
四、指针运算
4.1 指针+-整数
#define N_VALUES 5
float values[N_VALUES]
float* vp;
//指针+-整数
for(vp=&values[0];vp<&values[N_VALUES];)
{
*vp++=0;//后置++:先使用后++
}
4.2 指针-指针
int main()
{
int arr[10]={0};
printf("%d\n",&arr[0]-&arr[9]);
return 0;
}
打印结果为:9
|指针-指针|得到的是指针和指针之间元素的个数。
不是所有指针都能相减,只有指向同一块空间的两个指针才能相减。
4.3 指针的关系运算
#define N_VALUES 5
float values[N_VALUES]
float* vp;
//指针+-整数
for(vp=&values[N_VALUES];vp>&values[0];)
{
*--vp=0;
}
#define N_VALUES 5
float values[N_VALUES]
float* vp;
//指针+-整数
for(vp=&values[N_VALUES-1];vp>=&values[0];vp--)
{
*vp=0;
}
上述代码一和代码二都可行,原理如4.1指针+-整数的计算,但是代码二,标准并不保证它可行。
标准规定:允许指向数组元素的指针指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
五、二级指针
int main()
{
int a=10;
int* pa=&a;//pa是一个指针变量,一级指针变量
int** ppa=&pa;//ppa是一个二级指针变量
**ppa=20;
printf("%d\n",a);
return 0;
}
打印结果为:20
二级指针变量是用来存放一级指针变量的地址的。
六、指针数组
//存放指针的数组就是指针数组。
int main()
{
int a=10;
int b=20;
int c=30;
int* pa=&a;
int* pb=&b;
int* pc=&c;
return 0;
}
int main()
{
int arr1[4]={1,2,3,4};
int arr2[4]={2,3,4,5};
int arr3[4]={3,4,5,6};
int* parr[3]={arr1,arr2,arr3};
int i=0;
for(i=0;i<3;i++)
{
int j=0;
for(j=0;j<4;j++)
{
printf("%d ",parr[i][j]);
}
printf("\n");
}
return 0;
}
打印结果:1 2 3 4
2 3 4 5
3 4 5 6
i=0时,进入j循环:
i=0,j=0:i=0的意思是parr[0],即是parr指针数组的第0位是arr1
j=0的意思是arr1[0],即是arr1数组的第0位是1