不同语言变量以及参数的传递

变量以及参数传递

01.变量:作用域以及生命周期
      默认值以及可见性
      数据类型
          C语言:全局变量和局部变量 Global Variable  Local Variable
          Java语言:
                 类变量(静态变量):类中用static关键字声明
                 成员变量(属性, 实例变量)
                 局部变量(本地变量)在方法、构造器或者块中使用
          Scala: val  var 
          Python:
          R语言:
02.函数-方法等
    基本构成:
    参数传递: 根据语言的特点

1. C语言:

    在 C 语言中,函数的参数传递方式有两种:值传递与地址传递
      01.值传递:参数列表-函数名-返回值
        函数参数:使用变量、常量、数组元素
        函数体中的操作:将实参的值复制到形参相应的存储单元
                                      函数在调用时,隐含地把实参 a 的值赋值给了参数 x
        调用结束后:
                形参的存储单元被释放,而形参值的任何变化都不会影响到实参的值
                实参的存储单元仍保留并维持数值不变
        返回值:
    02.地址传递
        函数参数:数组名或者指针作为函数参数
          传递的是该数组的首地址或指针的值,而形参接收到的是地址

2. Java中

    在Java中,方法的传递方式
      数据类型: 基本数据类型 以及引用数据类型
            赋值操作:
                在基本数据类型中
                在引用数据类型中
            传值
                     传递的是值的副本。方法中对副本的修改,不会影响到调用方
            传引用:
                    01.传递的是引用的副本,共用一个内存,会影响到调用方。此时,形参和实参指向同一个内存地址。
                    02.如果对象被重新创建或赋值为null,即new则会重新指向其他对象,不影响其远对象的值
                    对引用副本本身(对象地址)的修改,如设置为null,重新指向其他对象,不会影响到调用方。
        01.传递:
            01.基本数据类型,传递的都是副本。在函数中改变了副本的值不会改变原始的值
                String
            02.对于引用类型(对象、数组),传引用            
        02.从内存模型来说参数传递:
                栈内存(stack)和堆内存(heap)
    总结:
        01.基本类型(byte,short,int,long,double,float,char,boolean)为传值
        02.String、Integer、Double等immutable类型因为类的变量设为final属性,无法被修改,只能重新赋值或生成对象。
           当Integer作为方法参数传递时,对其赋值会导致原有的引用被指向了方法内的栈地址,
          失去原有的的地址指向,所以对赋值后的Integer做任何操作都不会影响原有值。
        03.对象类型(Object,数组,容器)为传引用
    总结方式:
      01.在方法中,修改一个基础类型的参数永远不会影响原始参数值。
      02.在方法中,改变一个对象参数的引用永远不会影响到原始引用。
        然而,它会在堆中创建了一个全新的对象(指的是包装类和immutable对象)
      03.在方法中,修改一个对象的属性会影响原始对象参数。
      04.在方法中,修改集合和Maps会影响原始集合参数。

3.Scala

Scala的解释器在
    解析函数参数(function arguments)
     传值调用(call-by-value)
     传名调用(call-by-name)
    具体解释:
     传值调用(call-by-value):计算参数表达式的值(reduce the arguments),再应用到函数内部
    通常情况下,函数的参数是传值参数;即参数的值在它被传递给函数之前被确定
     传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部--惰性求值
    使用“按名称传递参数”方式的优点是:1.减少不必要的计算; 2.减少异常

4. Python中

    Python传递参数的方式:
       01.从表现上:Python参数传递采用的是“传对象引用”的方式
        如果函数收到的是一个
                    不可变对象(比如数字、字符或者元组)的引用,就不能 直接修改原始对象——相当于通过“传值”来传递对象
        如果函数收到的是一个
                    可变对象(比如字典 或者列表)的引用,就能修改对象的原始值——相当于通过“传引用”来传递对象。
                    可变对象的引用也可以不修改其对象的原始值--这个就要看具体的函数的操作了            
      02.从内存模型来说参数传递:其主要的关键的地方是了解:
        python(对可变对象和不可变对象的)赋值过程中是如何分配内存地址的。
             python中,不可变对象是共享的,创建可变对象永远是分配新地址

5. R语言中

    函数式编程:
        环境和变量作用域--函数可以作为参数
        函数可以作为参数传递
        函数式编程-函数不修改局部变量
        对参数进行重新赋值

代码示例

代码:--01.从具体表现角度-02.从内存模型角度
Python:
            x=[6,25,3]
            x.sort()
            print(x)     [3,6,25]
R 语言
        sort(x) [3,6,25]
        print(x) [6,25,3]
      如果想改变x的代码,可以通过对参数x进行重新赋值来解决
       即 x <- sort(x)
Java:
public  class FunctionTransfer {
 public static void main(String[] args){
    System.out.println("Hello,Java方法的传递方式-针对基本数据类型");
    // 虚拟机栈(VM Stack)--虚拟机栈、本地方法栈、程序计数器是线程独立的内存,每个线程只保存自己的数据。
    //用于实现方法调用,每次方法调用就对应栈中的一个栈帧,
    // 栈帧包含局部变量表、操作数栈、方法接口等于方法相关的信息。
    //栈中存放局部变量,参数,运行中间结果--形参是复制实参的一份拷贝
    // 具体--程序在栈内存中开辟了一块地址为AD8500的内存
    int num = 20;
    System.out.println("基本数据类型-调用前" + num);
    // // 栈的高度称为栈的深度,栈深度受栈帧大小影响--栈深度:11114
    //虚拟机栈中方法调用的深度超过虚拟机规定的最大深度,则抛出StackOverflowError,特别是方法的递归调用时
    //虚拟机栈能够容纳的栈帧数量是有限的,若栈帧不断进栈而不出栈,最终会导致当前线程虚拟机栈的内存空间耗尽,
    // 典型如一个无结束条件的递归函数调用
    // 虚拟机栈可以动态扩展,如果扩展时没有足够的内存则抛出OutOfMemoryError
    //OutOfMemoryError指的是当整个虚拟机栈内存耗尽,并且无法再申请到新的内存时抛出的异常
    add(num);
    // 程序在栈内存中又开辟了一块地址为AD8600的内存,将num的值30传递进来
    // 执行param = 100;后,AD8600中的值变成了100
    System.out.println("基本数据类型-调用后" + num);
    System.out.println("############################");
    System.out.println("Hello,Java方法的传递方式-针对引用数据类型");
    //方法区和堆是线程共享的内存
    //存储对象实例,java中通new创建的对象实例就保存在堆中
    // 对象的创建和存储--堆中分配一块内存来存储对象实例并初始化对象内存空间的值为
    // 访问对象通过对象的引用,引用保存在栈中
    //String 类型
    // String是一个引用类型,但是在作为参数传递的时候表现出来的却是基本类型的特性,
    // 即在方法中改变了String类型的变量的值后,不会影响方法外的String变量的值
    String s = "hello";
    System.out.println("Java方法的传递方式-针对引用数据类型String 前 s=" + s);
    changeString(s);
    System.out.println("Java方法的传递方式-针对引用数据类型String 后 s=" + s);
    System.out.println("############################");
    //数组对象作为参数传递 -- 复制引用的拷贝
    // 栈内存中开辟了一块地址编号为AD9500内存空间,用于存放array[0]的引用地址,
    //里边放的值是堆内存中的一个地址-示例中的值为BE2500,可以理解为有一个指针指向了堆内存中的编号为BE2500的地址
    //堆内存中编号为BE2500的这个地址中存放的才是array[0]的值
    String[] array = new String[] {"Hello-Do you know"};
    System.out.println("调用reset方法前array中的第0个元素的值是:" + array[0]);
    //程序在栈内存中又开辟了一块编号为AD9600的内存空间,栈内存AD9500和AD9600
    // (array[0]和param的值)都指向了编号为BE2500的堆内存
    reset(array);
    //改变对象param的值实际上是改变param这个栈内存所指向的堆内存中的值
    System.out.println("调用reset方法后array中的第0个元素的值是:" + array[0]);
    //当用对象作为参数传递时,真正的值是放在堆内存中的,传递的是栈内存中的值,
    // 而栈内存中存放的是堆内存的地址,所以传递的就是堆内存的地址。这就是它们的区别。

    //自定义的类的对象作为参数传递
    //对象作为参数传递时,传递的是对象的引用,也就是对象的地址
    //java中方法的参数传递机制
    //参考:https://www.cnblogs.com/lixiaolun/p/4311863.html
  }
public static void add(int param){
    param=1000;
  }
public static void changeString(String s) {// 改变String型变量的函数
    s = "changeString";
}
  public static void reset(String[] param) {
    param[0] = "hello, world!";
   }
}

blogroll

social