C/C++按值传递和按地址传递

2022-11-30 21:23:39 浏览数 (1)

C/C 的按值传递和按地址传递有明显不同,下面对他们作个区别:

按值传递:在调用函数中将原函数的值拷贝一份过去被调用的函数,在被调用函数中对该值的修改不会影响原函数的值。

按地址传递:在调用函数的时候将原函数的值所在的地址拷贝一份过去,被调用函数对这个地址所作的修改会影响原来的值。

鉴于本人表达能力有限,这样说可能有点迷惑,下面直接上图:

一、按值传递

代码语言:javascript复制
       #include<iostream>
      
     
     
      
     
     
      
       using 
       namespace std;
      
     
     
      
     
     
      
       void changeNumber(int x);
      
     
     
      
     
     
      
       int main(void)
      
     
     
      
     
     
      
       {
      
     
     
      
     
     
      	
       int a = 
       10;
      
     
     
      
     
     
      
       	cout << 
       "a = " << a << endl;
      
     
     
      
     
     
      	
       changeNumber(a);
      
     
     
      
     
     
      
       	cout << 
       "Now a = " << a << endl;
      
     
     
      
     
     
       
      
     
     
      
     
     
      	
       return 
       0;
      
     
     
      
     
     
      
       }
      
     
     
      
     
     
      
       void changeNumber(int x)
      
     
     
      
     
     
      
       {
      
     
     
      
     
     
      
       	x = x   
       5;
      
     
     
      
     
     
      
       }

这里是一个十分简单的程序,其中包含了一个changeNumber()函数,意图是改变在main()函数中变量 a 的值,显而易见,这是一种按值传递,changeNumber()函数不可能完成任务:

可以看到,a并没有被改变。原因很简单,按值传递,当调用changeNumber()函数的时候,以 a 作为实际参数,在程序跳到changeNumber()函数的时候,会拷贝一份a的数据,也就是说,在内存空间中重新开辟了一块空间,它的作用就是用来存储a的值,并且这块空间的名称为 x,也就是changeNumber()中的这个x,注意,这个x所在的空间和a所在的空间是完全不同的两块内存(通过对他们分别输出地址可以得出),既然他们根本不属于同个地方,那么对x所作的任何修改都无法影响a。

二、按地址传递

如果这么写代码:

代码语言:javascript复制
       #include<iostream>
      
     
     
      
     
     
      
       using 
       namespace std;
      
     
     
      
     
     
      
       void changeNumber(int* x);
      
     
     
      
     
     
      
       int main(void)
      
     
     
      
     
     
      
       {
      
     
     
      
     
     
      	
       int a = 
       10;
      
     
     
      
     
     
      
       	cout << 
       "a = " << a << endl;
      
     
     
      
     
     
      	
       changeNumber(&a);
      
     
     
      
     
     
      
       	cout << 
       "Now a = " << a << endl;
      
     
     
      
     
     
      	
       return 
       0;
      
     
     
      
     
     
      
       }
      
     
     
      
     
     
      
       void changeNumber(int* x)
      
     
     
      
     
     
      
       {
      
     
     
      
     
     
      
       	*x = *x   
       5;
      
     
     
      
     
     
      
       }

这里的代码就是按地址传递,首先在changeNumber()的声明中,使用了int*类型而不是int类型,其次在main()函数调用changeNumber()函数的时候使用的实参是&a而不是a,这就传递了一个地址给changeNumber()函数,这个changeNumber()就可以完成修改a的任务:

按照我的理解,按地址传递实际上是一种特殊的按值传递,原因如下:

1.main()函数调用changeNumber()的时候,将a的地址,也就是&a传递给changeNumber()函数,这实际上就是将a的地址拷贝一份过去给changeNumber()函数,在前面的例子中,main()拷贝并传递的是a,这里main()拷贝并传递的是&a,两者都可以理解为按值传递,只不过第二个例子中的“值”是a的地址而已

2.changeNumber()函数被调用之后,它根据传过来的地址值(请注意,这里说的是地址值,因为地址本身也是一个可见的数值),在内存中开辟一块新的空间,这块空间是用来存储这个地址值的,换句话说,这块空间上的内容就是a的地址,这似乎有点难以理解,但事实就是这样的,然后使用 * 运算符对x进行操作,*x操作的就是a的具体的值,而x作为一个指针,它存有a的地址(或者说它指向a的地址),那么对*x所做的任何操作都会影响a的值。

拓展:

    对于第二个例子,如果使用cout输出x,结果得到的将会是一个地址,并且这个地址与a的相同,这符合我前面所叙述的内容。

代码语言:javascript复制
       #include<iostream>
      
     
     
      
     
     
      
       using 
       namespace std;
      
     
     
      
     
     
      
       void changeNumber(int* x);
      
     
     
      
     
     
      
       int main(void)
      
     
     
      
     
     
      
       {
      
     
     
      
     
     
      	
       int a = 
       10;
      
     
     
      
     
     
      
       	cout << 
       "a = " << a << endl;
      
     
     
      
     
     
      	
       changeNumber(&a);
      
     
     
      
     
     
      
       	cout << 
       "Now a = " << a << endl;
      
     
     
      
     
     
       
      
     
     
      
     
     
      
       	cout << 
       "&a = " << &a << endl;
      
     
     
      
     
     
       
      
     
     
      
     
     
      	
       return 
       0;
      
     
     
      
     
     
      
       }
      
     
     
      
     
     
      
       void changeNumber(int* x)
      
     
     
      
     
     
      
       {
      
     
     
      
     
     
      
       	*x = *x   
       5;
      
     
     
      
     
     
      
       	cout << 
       "x = " << x << endl;
      
     
     
      
     
     
      	
      
     
     
      
     
     
      
       }

好,现在把注意点集中到这行代码上:

代码语言:javascript复制
cout << "&x = " << &x << endl;

对这个&x进行输出,结果得到的又是另一个地址,事实上,这个地址就是调用changeNumber()函数时分配的内存空间,这块内存空间存储的便是x也是a的地址,当函数结束后,这块空间会被收回,上面的x的内容也会消失。

前面说了x存放的是a的地址,也就是说,x指向a的地址,如果有操作:

代码语言:javascript复制
x = x   5

把这行代码添加到changeNumber()函数的最前面,即:

代码语言:javascript复制
代码语言:javascript复制
        void changeNumber(int* x)
       
      
      
       
      
      
       
        {
       
      
      
       
      
      
       
                x = x   
        5;
       
      
      
       
      
      
       
        	*x = *x   
        5;
       
      
      
       
      
      
       
        	cout << 
        "x = " << x << endl;
       
      
      
       
      
      
       
        }

这样会发生的事情是:这个函数又无法完成修改main中a的值了,原因就是,x存放的不再是a的地址,x的值已经发生了移动,换句话说,x指向了别的地址。

由于C和C 语法的相似性,经过C环境下写出的代码,同样符合这个逻辑

之所以写这个东西,是因为看到有博主写了关于Java按值传递和按引用传递的文章,链接:

https://blog.csdn.net/javazejian/article/details/51192130

刚好最近在学Java,文章所述Java的按值传递和按引用传递使我对C 按值传递和按地址传递的理解产生了疑问,一番实验之后终于弄清楚了。原文章写的非常不错,建议同样正在学习Java的同学可以看一看。

0 人点赞