为什么要用指针,什么时候该用指针,什么时候该用指针的指针
很多内容可以参考同专栏的其他文章,像指针&结构体、字符串,函数指针,数组指针,指针基础概念与内存分配等。
序
几个必须了解的前提概念:
(1)指针是一个保存地址的变量。这个地址是指针所指向的变量的地址而不是指针本身的地址。
(2)C语言的参数传递方式有值传递和地址传递。
(3)C语言中参数传递的本质都是值传递。
(4)形参在调用返回时就会被销毁,它只是拷贝了一份实参,形参的改变对实参没有影响。
(*5)一个实参指针变量传入函数的形参指针变量时,指针指向的内容是可以改变的,但是指针的值和地址本身是不会改变的。
(*6)C语言的变量一但声明赋值后,就会分配一块内存,这块内存的地址是不会改变的。
(7)数组作为形参传递时,形参实参是同一块内存。
一、什么时候该用指针
(1)动态数组
指针做动态数组声明用:int *arr;
:指针名就是数组名,直接解引*arr
就是数组的首项值。
(2)一些功能性声明
函数指针或数组指针。
(3)传参改变变量
当要调用函数改变一个变量的值时,就要用到指针。因为形参不改变实参。
例如:
void swapValue(int num1,int num2)
{
int temp;
temp = num1;
num1 = num2;
num2 = temp;
printf("-------swapValue-------\n\r");
printf("num1=%d\n\r",num1);
printf("num2=%d\n\r",num2);
}
void swapPoint(int *p1,int *p2)
{
int temp;
temp = *p1; //tmp=a
*p1 = *p2; //a=b
*p2 = temp; //b=tmp
printf("-------swapPoint-------\n\r");
printf("*p1=%d\n\r",*p1);
printf("*p2=%d\n\r",*p2);
}
void swapPoint2(int *p1,int *p2)
{
int *temp;
temp = p1; //tmp=&a
p1 = p2; //&a=&b
p2 = temp; //&b=tmp
printf("-------swapPoint2-------\n\r");
printf("*p1=%d\n\r",*p1);
printf("*p2=%d\n\r",*p2);
}
int main()
{
int a = 5;
int b = 10;
printf("a=%d\n",a);
printf("b=%d\n",b);
printf("aAdd=%p\n",&a);
printf("bAdd=%p\n",&b);
swapValue(a,b);
printf("a=%d\n",a);
printf("b=%d\n",b);
printf("aAdd=%p\n",&a);
printf("bAdd=%p\n",&b);
swapPoint(&a,&b);
printf("a=%d\n",a);
printf("b=%d\n",b);
printf("aAdd=%p\n",&a);
printf("bAdd=%p\n",&b);
swapPoint2(&a,&b);
printf("a=%d\n",a);
printf("b=%d\n",b);
printf("aAdd=%p\n",&a);
printf("bAdd=%p\n",&b);
return 0;
}
可自己先演算一下结果再与下方结果对比。
编译结果:
逐个分析:
swapValue
:传递的是值,形参再怎么变化,函数返回时就会从栈中销毁,因此只会改变形参num1,2的值,不会改变a,b的值,地址就更不用说了。
swapPoint
:传递的是指针,这下形参是指针,因此指针便不会改变,但是指针所指向的内容可以改变,因此a,b的值就发生了互换,但是地址不变化。
swapPoint2
:传递的是指针,同swapPoint
一样,但是内部是直接对形参的指针变量进行交换操作,即互换地址,因此解引形参时相当于将前面调换的值调换回来了。但是函数返回时一切销毁,所以a,b的值和地址都不会变化,而且C语言定义的变量地址是无法改变的。
二、什么时候该用指针的指针
(1)通过传递二级指针改变量
通过传入指向一块内存的地址值的指针(即指针的指针),间接修改指向的值。
例如:
void swap2Pointer(int** p1,int** p2)
{
int temp;
printf("-------into swapAddr-------\n\r");
printf("**p1=%d\n\r",**p1); //p1=&x,*p1=&a,**p1=a
printf("**p2=%d\n\r",**p2);
temp = **p1;
**p1 = **p2;
**p2 = temp;
printf("-------after swapAddr-------\n\r");
printf("**p1=%d\n\r",**p1);
printf("**p2=%d\n\r",**p2);
}
int main()
{
int a = 5;
int b = 10;
printf("a=%d\n",a);
printf("b=%d\n",b);
printf("aAdd=%p\n",&a);
printf("bAdd=%p\n",&b);
int *x = &a;
int *y = &b;
swap2Pointer(&x,&y);
printf("a=%d\n",a);
printf("b=%d\n",b);
printf("aAdd=%p\n",&a);
printf("bAdd=%p\n",&b);
}
编译结果:
拓展:上面说过传入形参的实参是不会改变的(因为函数返回时就销毁),但是指向的内存是可以改变的(现在指向的是a的地址的值),那么将上述swap2Pointer
修改为如下swapAddr
是不是就能改变*p1(&a)
和*p2(&b)
了,从而调换x和y指向的地址值,答案是可以的。
如下:
void swapAddr(int** p1,int** p2)
{
int temp;
printf("-------into swapAddr-------\n\r");
printf("*p1=%x\n\r",*p1); //p1=&x,*p1=&a,**p1=a
printf("*p2=%x\n\r",*p2);
temp = *p1;
*p1 = *p2;
*p2 = temp;
printf("-------after swapAddr-------\n\r");
printf("*p1=%x\n\r",*p1);
printf("*p2=%x\n\r",*p2);
}
int main()
{
int a = 5;
int b = 10;
printf("a=%d\n",a);
printf("b=%d\n",b);
printf("aAdd=%p\n",&a);
printf("bAdd=%p\n",&b);
int *x = &a;
int *y = &b;
printf("*x=%d\n",*x);
printf("*y=%d\n",*y);
swapAddr(&x,&y);
printf("a=%d\n",a);
printf("b=%d\n",b);
printf("aAdd=%p\n",&a);
printf("bAdd=%p\n",&b);
printf("*x=%d\n",*x);
printf("*y=%d\n",*y);
}
编译如下:
(2)无法靠传一级指针改变指针时
像如下代码编译时会奔溃,因为num传入p时,num是无法被改变的,因此num没有开辟出空间无法赋值。
void pMalloc(int *p,int size)
{
p = (int *)malloc(size*sizeof(int));
}
int main()
{
int *num = NULL; //如果是全局和static会自动初始化为NULL
pMalloc(num,5);
if(num!=NULL)
{
*num = 200;
printf("num=%d",*num);
}
else
{
printf("Assignment Fail");
}
return 0;
}
编译结果:
而如果将传入的参数变成实参变量的地址的话:
void pMalloc(int **p,int size)
{
*p = (int *)malloc(size*sizeof(int));
}
int main()
{
int *num = NULL; //如果是全局和static会自动初始化为NULL
pMalloc(&num,5); //p=#*p=num;**p=*num
if(num!=NULL)
{
*num = 200;
printf("num=%d",*num);
}
else
{
printf("Assignment Fail");
}
return 0;
}
编译结果:(成功)
记住前面序中的几点概念,那么玩转指针就很简单了。而多级指针如果很乱的话,就像上面程序中p=#*p=num;**p=*num
这样抽丝剥茧即可。
(3)更新ing…
结
若想通过函数修改变量值,可以通过传递指针以修改目标地址上存储的值,而变量的地址是不能改变的。不管是几级指针,最终改变的一定是对应地址上的内存,即以"值"形式存在的数据。