美文网首页
值传递、地址传递的笔记

值传递、地址传递的笔记

作者: 大成小栈 | 来源:发表于2021-04-25 21:44 被阅读0次

在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会成为野指针!

注意:一级指针、二级指针作为参数时,入参及函数内使用参数的形式不同。

原文链接:https://blog.csdn.net/ye1223/article/details/79674975

相关文章

网友评论

      本文标题:值传递、地址传递的笔记

      本文链接:https://www.haomeiwen.com/subject/pvjjrltx.html