• java方法中,传参是传值还是传址问题(对比C语言、C#和C++)


    问题引出:


    编写一个简单的交换值的小程序,如果我们只是简单地定义一个交换函数接收两个数,在函数内部定义一个中间变量完成交换。那么当我们把a,b两个实参传给这个函数时,往往得不到预期的结果。这是为什么呢?

    在C语言和C#中:


    在C语言中,如果我们运行下列代码:

    #include<stdio.h>  
    
    void swap(int,int);  
      
    void main(){  
      
      int a=5;  
      
      int b=7;  
      
     printf("a=%d b=%d
    ",a,b);//a=5 b=7  
      
     swap(a,b);  
      
     printf("a=%d b=%d
    ",a,b);//a=5 b=7  
      
    }  
      
    void swap(int c,int d){  
      
     int temp;  
      
     temp=c;  
      
     c=d;  
      
     d=temp;  
      
    }

    以及在C#中运行下列代码并试着传进去a,b:

     static void swap(int c, int d)  //这个函数将以传值的方式运行   
      
     {  
      
    int temp;  
      
    temp=c;  
      
    c=d;  
      
    d=temp;  
      
     }  

    得到的结果都不是我们预期的那样实现a和b的数值交换。

    这就是因为在C和C#中存在传值和传址两个不同的概念。

    以上代码实现的是“传值”的概念。也就是说,在程序运行时,实参将自己的值复制给形参,而形参也是分配内存空间的。因此在这个值传递的过程过后,实参和形参实际上就指向了两个不同的存储空间,不存在关联关系了。在我们所谓的执行交换值的函数中,交换的实际上是形参而已(不是a,b而是c,d),并且形参作为局部变量的生命周期也就是只在函数内部,函数运行结束,形参也就不复存在了。故代码达不到我们交换值的目的。

    对于这个问题的解决,C语言和C#分别提供的解决方案之一就是址传递

    在C语言中,我们可以很简单地利用别名就可以完成两个数的交换:

    #include<stdio.h>  
    
    void swap(int &,int &);  
      
    void main(){  
      
      int a=5;  
      
      int b=7;  
      
     printf("a=%d b=%d
    ",a,b);//a=5 b=7  
      
     swap(a,b);
      
       
      
     printf("a=%d b=%d
    ",a,b);//a=7 b=5  
      
    }  
      
       
      
    void swap(int &a,int &b){ //此处利用取地址符,传入地址 
    int temp; temp=a; a=b; b=temp; }

    在C#中,提供了专门的关键字ref可以表示是传值还是传地址。

    static void swap(ref int i, ref int t) // 这个函数将会以传址的方式进行运算.         
    {
        int temp;  
        temp=a;  
        a=b;  
        b=temp;  
     }

    以上两段代码实现的就是传址的方式。可以实现我们的预期代码实现功能。

    在C语言中还可以使用操作指针的方式(但是属于值传递)达到预期

    #include<stdio.h>  
    
    void swap(int *,int *);  
      
    void main(){  
      
      inta=5;  
      
      intb=7;  
      
     printf("a=%d b=%d
    ",a,b);//a=5 b=7  
      
     swap(&a,&b);//此处传出a,b的地址,利用取地址符  
      
       
      
     printf("a=%d b=%d
    ",a,b);//a=7 b=5  
      
    }  
      
       
      
    void swap(int *a,int *b){//传入地址,就可以完成两个说  
      
     inttemp;  
      
     temp=*a;  
      
     *a=*b;  
      
     *b=temp;  
      
    } 

    无论是C语言中的别名,还是C#中的关键字ref。传递的都是实参的地址,也就是说程序运行时,实参将自己的地址传递给函数。这时的实参和形参所指向的地址是一致的,也就是说是一个内存空间上的对象,那么在函数运行期间对形参的操作也就顺理成章的改变了实参的值。

    那么对于java呢?


    先贴个例子:

    package com.test;
    
    public class Test1 {
        
        
        public static void Reverse(int R[],int l,int r){
            int i,j;
            int temp;
            for(i=l,j=r;i<j;++i,--j){
                temp=R[i];
                R[i]=R[j];
                R[j]=temp;
            }
        }
    
        public static void Add(int a,int b){
            a=a+100;
            b=b+1000;
        }
        
        
        public static void main(String[] args) {
    
            int[] array=new int[]{1,2,3,4,5,6};
            Reverse(array,0,5);
            for(int i=0;i<array.length;++i)
            System.out.print(array[i]);
         System.out.println(); System.out.println(
    "——————————————"); int i=1,j=2; Add(i,j); System.out.println(i+"####"+j); } }

    输出结果:

    654321
    ——————————————
    1####2

    由于Java中程序员无法直接操作指针,对于传值还是传地址很模糊,但我们知道java中变量分为两类,一类是基本变量,一类是引用。(数组属于引用)

    其实当我们把实参a,b传进方法后,就相当于把a,b的值分别传给了方法用于接收a,b的局部变量,那么不管是传进去引用还是值,实参a,b的值都不会被改变,因为操作的时函数中接收a,b传递的值的局部变量,函数执行完毕,这两个局部变量也就不存在了。

    那为什么传进去引用可以修改a,b的值呢,这是不违反上面说的原理了吗?

    其实,Java中无论传值还是传引用都是传的参数的副本,即将实参进行复制。副本只在函数内部有效。而当传引用时,传进去的是实参自己的地址的复制版,地址被复制了,但是指向的值的指向没变(即对于“地址”这个东西有实参和形参不指向同一存储空间这回事,但是对于“地址指向的值”只有一个),同时地址无法被改变但是地址指向的值可以被改变。所以可以说java中都是在传值。

    在C++中:


    在C++中,允许操作指针。也存在着明确的值传递和址传递。(其实下面两条在概念上对于所有这几种语言来说是普适的)

    值传递(按值传递):  1.实参值传递给相应形参 2.实参地址传递给相应形参 比如:数组、指针。(类似Java)

    址传递(引用传递):   使用别名,共享存储空间(直接访问) 形参为引用参数时,才为按址传递,此时对应实参一般为一个变量。

    强调:除了使用别名外,即使是使用指针也是值传递。即在函数的形参列表中,只要存在形参存储空间的开辟就属于值传递。

    贴两个例子:

    例1:

    #include<iostream>
    using namespace std;
    void Reverse(int R[],int l,int r){
        int i,j;
        int temp;
        for(i=l,j=r;i<j;++i,--j){
        temp=R[i];
        R[i]=R[j];
        R[j]=temp;
        }
    }
    
    void Add(int c,int d){
    c=c+100;
    d=d+1000;
    }
    int main(){
    
        int array[]={1,2,3,4,5,6};
        Reverse(array,0,5);
        for(int i=0;i<6;++i){
        cout<<array[i];
        }
        cout<<endl;
        int a=1,b=2;
        Add(a,b);
        cout<<a<<","<<b;
        cout<<endl;
        return 0;
    }

    运行结果:

    例2(代码来自于参考博客):

    #include<iostream>  
    using namespace std;  
    void mySwap(int *p1,int *p2);  
    int main(){  
      
        int a=12;  
        int b=44;  
      
        int *pa=&a;  
        int *pb=&b;  
      
      
        if(a<b){  
          
            mySwap1(a,b);  
            //mySwap2(pa,pb);  
            //mySwap3(pa,pb);  
            //mySwap4(a,b);  
        }  
      
      
        return 0;  
    }  
      
    /*  
    int类型作为形参--值传递:形参a ,b 也要分配内存空间,实参的值复制给形参  
    */  
    void mySwap1(int a,int b){  
        int temp;  
        temp=a;  
        a=b;  
        b=temp;  
    }  
    /*  
    int * 类型作为形参--值传递:形参p1,p2 也要分配内存空间,实参的值复制给形参(地址)  
    */  
    void mySwap2(int *p1,int *p2){  
      
        //改变形参指针的指向,不会影响到实参  
        int *temp;  
        temp=p1;  
        p1=p2;  
        p2=temp;  
      
    }  
    /*  
    int * 类型作为形参--值传递:形参p1,p2 也要分配内存空间,实参的值复制给形参(地址)  
    */  
    void mySwap3(int *p1,int *p2){  
      
        //改变形参指针指向的内存空间值,也就改变了实参指针指向的内存空间值  
        int temp2;  
        temp2=*p1;  
        *p1=*p2;  
        *p2=temp2;  
    }  
      
    /*  
    引用类型作为形参--址传递:形参a,b是不分配内存空间的,形参是实参的“别名”  
    */  
    void mySwap4(int &a,int &b){  
      
        int temp;  
        temp=a;  
        a=b;  
        b=temp;  

    总结一下:


    C语言、C#、C++中有明确的传值和传址方式。程序员可以主动区分。

    在C语言和C++中,除了使用别名外,其他都属于值传递。

    在Java中,由于不能直接操作指针,只存在值传递,但是当传递的值是引用时,那么方法中得到的是引用的地址的复制版,地址被复制了,但是指向的值的指向没变(即对于“地址”这个东西有实参和形参不指向同一存储空间这回事,但是对于“地址指向的值”只有一个),同时地址无法被改变但是地址指向的值可以被改变。所以可以说java中都是在传值。

    那么,回到最初的问题上面来,我们要怎么做才能实现两个数值的交换呢?


    小编的答案是操作地址,无论是值传递还是址传递,只要操作的是地址,那么形参和实参指向的就是同一个存储空间,也就是说形参的改变可以带来实参的改变。

    那么,请各位想一想怎样实现Java中基本变量的数值交换呢?小编还没想好,欢迎大家赐教。

    参考博客:

    http://blog.csdn.net/single0515/article/details/51584536

    http://blog.csdn.net/cxc19890214/article/details/44617685

    http://blog.csdn.net/u010126792/article/details/63771475

  • 相关阅读:
    Dive into ML
    tensorflow中模型的保存
    scope 命名方法
    Tensorflow中的Seq2Seq全家桶
    梯度消失与梯度爆炸问题
    Tensorflow 模型的保存、读取和冻结、执行
    训练神经网络的处方
    About Feature Scaling and Normalization
    论文中绘制神经网络的工具
    前端 OSS 自动化部署脚本
  • 原文地址:https://www.cnblogs.com/dudududu/p/8509230.html
Copyright © 2020-2023  润新知