在C语言中,理解值传递、地址传递和引用传递非常重要,首先,我们得来区分一下指针变量、指针地址、指针的值之间的关系。有三个变量:
C是"一段内容";
B是一个指针变量,存放着C的地址;
A是一个二级指针变量,存放着B的地址。如下图所示:
左侧为地址,右侧为对应值
B = 0x00000008;// B的内容
*B = "一段内容";// B解引用,也就是B指针指向的C的值
&B = 0x00000004;//B取地址,B的地址是0x00000004
A = 0x00000004;// A的内容
*A = B= 0x00000008;// A解引用也就是B的内容
&A = 0x00000000;// A取地址,A的地址是0x00000000
**A = *B = "一段内容";// B解引用,也就是B指针指向的C的值
A = &B = 0x00000004;// A存的是B的地址,B的地址是0x00000004
以此,形如**A的指针被称为二级指针,形如*B的指针被称为一级指针。
一级指针作参传递
代码中定义了三个同样类型的变量(a、b是值,q是整型指针),观察为什么执行log中*q不等于100?
#include<iostream>
using namespace std;
int a= 10;
int b = 100;
int *q;
void func(int *p) {
cout<<"func:&p="<<&p<<",p="<<p<<endl; //note:3
p = &b;
cout<<"func:&p="<<&p<<",p="<<p<<endl; //note:4
}
int main()
{
cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl; //note:1
q = &a;
cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl; //note:2
func(q);
cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl; //note:5
system("pause");
return 0;
}
---------------------------------------------------------------
//// q = &a时,q的值变了;但是func(q)之后,q的值没变!!!
&a=0032F000,&b=0032F004,&q=0032F228
*q=10,q=0032F000,&q=0032F228
func:&p=0018FD24,p=0032F000
func:&p=0018FD24,p=0032F004
*q=10,q=0032F000,&q=0032F228
我们看输出:
note:1->a,b,q都有一个地址;note:2->q指向a;note:3->我们发现参数p的地址变了,跟q不一样了,但是其指向的地址0x0032F000(a的地址)还是不变的;note:4->p重新指向b;note:5->退出函数,p的修改并不会对q造成影响。说明参数传递是制作了一个副本,也就是p和q不是同一个指针。
结论:
编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 p,编译器使 p = q(但是&p != &q,也就是他们并不在同一块内存地址,只是他们的内容一样,都是a的地址)。如果函数体内的程序修改了p的内容(比如在这里它指向b)。在本例中,p申请了新的内存,只是把 p所指的内存地址改变了(变成了b的地址,但是q指向的内存地址没有影响),所以在这里并不影响函数外的指针q。
二级指针作参传递
二级指针本身也是一个变量,即指针的指针。想要函数内的修改,导致外部参数联动,那就需要用二级指针作参传递。
#include<iostream>
using namespace std;
int a= 10;
int b = 100;
int *q;
void func(int **p) //2
{
cout<<"func:&p="<<&p<<",p="<<p<<endl;
*p = &b; //3
cout<<"func:&p="<<&p<<",p="<<p<<endl;
}
int main()
{
cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl;
q = &a;
cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;
func(&q); //1
cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;
system("pause");
return 0;
}
参照开始的示意图可知:
**A = *B = "一段内容";// B解引用,也就是B指针指向的C的值
A = &B = 0x00000004;// A存的是B的地址,B的地址是0x00000004
其中,A本身是一个指针变量,储存的始终是另一个指针的地址。本例中传参时,p本身依然是编译器创建为方法临时副本,但p为二级指针,传入并保存的值是q的地址;p和q保存在不同的内存区域;p=&q,因此当进行
*p=&b时,即进行的是q=&b,改变了指针q的指向。
例子
下面代码,通过调用子函数,为主函数指针分配一块内存空间:
void my_malloc(char **s) {
*s=(char*)malloc(100);
}
void main() {
char *p=NULL;
my_malloc(&p);
free(p);
p=NULL;
}
在调用my_malloc时,实参值为&p,即指针p的地址;my_malloc执行时,分配临时变量
s=&p;*s=(char*)malloc(100)操作等同于:p=(char*)malloc(100);即通过调用子函数,为主函数指针分配一块内存空间。
注意:如果malloc函数被调用,则后续函数中一定需要有free将对应的内存释放,否则可能导致内存泄露;当free(p)后,需要让p=NULL,否则指针p会成为野指针!
注意:一级指针、二级指针作为参数时,入参及函数内使用参数的形式不同。









网友评论