精华内容
下载资源
问答
  • 文章目录数据和变量分类数据是怎么存的实参与形参引用传递和值传递传递引用传递结合存储方式做理解特殊情况 数据和变量分类 首先,我们需要大概明白,java中数据类型分为两类,基本类型与对象类型(一些特殊...

    数据和变量的分类

    首先,我们需要大概明白,java中的数据类型分为两类,基本类型对象类型(一些特殊的如String后续讨论)

    基本类型:byte,short,int,long,char,float,double,boolean,returnAddress
    对象类型:类类型,接口类型和数组

    以上数据类型是针对数据本身特点而言的,如果我们要使用他们,也就是数据成为了变量,此时结合数据在内存中的保存方式,对应的叫法成为了基本类型引用类型
    基本类型保存的是对象本身的值,而引用类型保存的是引用的值,也就是对象地址的值。

    数据是怎么存的

    数据是怎么存的,其实是个很复杂的话题,需要涉及到很多java内存模型的知识,此处不再深入拓展。从以下场景的代码来简单说明下

    // 首先需要明白,People这个对象实例化就是放在堆中的,所以它里面的成员变量自然也是在堆中的。
    Class People{
    	// age是一个基本类型
    	int age;
    	// child是一个引用类型
    	People child;
    	void setAge(int age){
    		this.age = age;
    	}
    	void setChild(int child){
    		this.child= child;
    	}
    }
    Class People Test(){
    	public void createPeople(){
    		// 这一步只是声明了一个引用类型的变量man,并为他在方法栈中分配了内存空间,用来存放之后对象的地址值
    		People man;
    		// 这一步在内存中创建了一个对象,并将对象的地址值(比如A0X456465之类的)赋值给了man这个引用变量 
    		man = new People()
    		// 这一步是在方法栈中声明了一个基本类型的变量age,并为他分配了内存用来存储数据本身。
    		int age;
    		// 这里的10是存在方法栈中的。
    		age = 10;
    		// 这里将方法区中定义并赋值的基本变量age的值赋值给了引用变量man所指向的对象的成员变量age。所以man.age的值是存在堆中的。
    		man.setAge(age);
    		// 这里将一个堆中对象child的地址值赋值给了man中的引用成员变量child。所以其实man.child与方法中定义的child这两个引用变量,指向的是同一个堆中的对象所在的地址
    		People child = new People;
    		man.setChild(child);
    		
    	}
    }
    

    实参与形参

    // 这里的age是实参
    int age = 10;
    // 方法签名里的就是形参
    setAge(age);
    

    引用传递和值传递

    值传递

    值传递就是实参把它的值传给形参,基本数据类型都是值传递,即函数接受的是实参的一个copy,内存中存在两份参数,也就是两份变量和值,方法中对参数的修改不会影响原本实参的值

    引用传递

    引用传递就是实参把引用传给形参,对象类型都是引用传递,即函数接受的是对象的地址值,内存中存在一份参数,一份变量和值,方法中对参数的修改会影响原本实参的值

    结合存储方式做理解

    其实造成这种区别的原因,结合上面数据的存储方式就很好理解了,不管是那种传递,传递本身的操作是一样的。
    比如我定义了一个变量a,传递给方法test(),就是把这个变量a的值copy一份传给test()。
    只不过当变量a的类型是基本数据类型的时候,a存储的就是值本身,传递的是一份原始值的copy,所以我们在方法中对传进来的a做修改,并不会影响到最初的a。
    当变量a的类型是引用类型的时候,a存储的是引用本身,传的自然是引用的copy,我们在方法中对a做修改的时候,如果是对a指向的对象做修改(比如a.setAge(50)这种操作,这里假设age是一个成员变量),那么由于两个引用指向的是同一块堆中的内存,自然是有影响的。
    如果是a = new A()这种操作,是不会影响到之前的a的,因为它没有对之前堆中对象做修改,只是改变了引用本身的值。

    特殊情况

    对于String、Integer、Double等几个基本类型包装类,都是immutable类型(为了深度拷贝方便),尽管他们是引用类型的变量,但是由于没有提供自身修改的函数,所以每次对对象的修改操作都会形成新的对象,即在堆中开辟新的内存,所以它的效果和值传递是一样的

    展开全文
  • 首先我们先通过一题来了解一下实参与形参间值传递的方式。例题1:void Getmemory(char *p) { p=(char *)malloc(100); } int main() { char *str=NULL; Getmemory(str); strcpy(str,"hello!"); cout&...

    首先我们先通过一题来了解一下实参与形参间值传递的方式。

    例题1:

    void Getmemory(char *p)
    {
        p=(char *)malloc(100);
    }
    int main()
    {
        char *str=NULL;
        Getmemory(str);
        strcpy(str,"hello!");
        cout<<str<<endl;
    }
    

    此题无法正确输出结果“hellow!”。

    原因是:在main函数中,首先生成一个指针str指向NULL,然后将str指针传入Getmemory函数。注意!此时在Getmemory函数中的指针p是str的一个副本。即str指向了NULL,p也指向了NULL,但是在Getmemory中执行分配内存的是p指针,str并没有被分配内存!所以接下来的strcpy函数无法正确执行。

    那么该如何解决这个问题,使str能够正确分配到内存呢?

    方法一:

    采用return的方式,将Getmemory运行的结果返还给main函数。

    char *Getmemory(char *p)
    {
        return p=(char *)malloc(100);
    }
    int main()
    {
        char *str=NULL;
        str=Getmemory(str);
        strcpy(str,"hello!");
        cout<<str<<endl;
    }   

    我们将分配好内存的p指针返还给main函数,赋值给str指针,所以str也分配好了内存。如果函数体返回的值不止一个怎么办呢?

    通过方法一,我们可以提前生成一个结构体,其成员函数是所有要返回的值,return这个结构体就行。

    方法二:

    采用二级指针的方式,即指针的指针,将p指针的地址p1(1、p指针自身同样占有内存空间,p1指向p 2、地址也是个指针)作为参数传入函数中,生成副本指针p2,p2指针同样指向p指针所在的内存空间,即p2也指向p。在传入的函数中对p2所指向的内容(即p指针)进行更改就可以改变p的赋值。

    void Getmemory(char **p)
    {
        *p=(char *)malloc(100);
    }
    int main()
    {
        char *str=NULL;
        Getmemory(&str);
        strcpy(str,"hello!");
        cout<<str<<endl;
    }
    

    我们可以发现,将str指针的地址传入Getmemory函数中,生成一个副本指针p,此时p指向的值为str指针。我们的目标是给str指针分配内存空间,所以要对*p(取内容)进行内存分配,而不是对p本身进行内存分配。指针关系如下图所示。









    拓展:

    对指针的操作分为两部分,一个是对指针自身进行操作,第二个是对指针指向的内容进行操作。

    方法三:

    在C++中,可以采用引用的方式来处理(C中不可以,没有引用的概念)。将b声明为a的引用,不需要为b另外开辟内存单元,b和a占内存中的同一个存储单元。(int a=1;int &b=a;      char *p=NULL; char *&p2=p;)

    void Getmemory(char *&p)
    {
         p=(char *)malloc(100);
    }
    int main()
    {
        char *str=NULL;
        Getmemory(str);
        strcpy(str,"hello!");
        cout<<str<<endl;
    }

    将指针str的引用指针p传入Getmemory中,对指针p的改动同样就是对指针str的改动,p是str的别名。

    例题2:

    char *Getmemory()
    {
        char p[]="hello!";
        return p;
    }
    int main()
    {
        char *str=NULL;
        str=Getmemory();
        cout<<str<<endl;
    }
    

    此题能否正确打印hello呢?答案是否。这是为什么呢?上文你不是说通过return返回就行了吗?

    这是因为,指针p确确实实返回给了str,但是指针p指向的内容“hello!”已经消亡了。在Getmemory函数中数组p为局部变量,其生存周期为函数的执行期间,当函数执行完毕,数组p就消亡了,那么指针p指向的内容就没有了。

    那么该如何解决呢?

    static。利用static修饰数组p,使其成为静态局部变量,改变它的生存周期,使其生存周期扩展到该CPP运行结束,那么此时str接收到的即为p数组的首地址,数组p也没有消亡,实现打印。

      static char p[]="hello!";

    例题3:

    如果把例题2的 char p[]="hello!";换成 char *p="hello!"; 呢?

    答案是可以正常打印。这又是为什么呢?

    这是因为,在例题2中“hello!”存储在名为p的数组中,数组p是局部变量,随着函数执行结束消亡。但是在本题中,“hello!”是个字符串,存储在常量区中,我们只是用一个指针p去指向它。return后,指针p消亡了,但字符串仍在常量区中,指针str指向字符串。所以可打印。


    展开全文
  • 实参形参 C++

    2012-09-21 22:50:00
    传递方式: 1、普通调用,形参与...在函数中可以修改形参的值,但不能改变其对应的实参的值(形参的值变化无法传送到实参) 原因:在函数调用时将建立一个新的对象,是实参对象的拷贝,实参与形参分别占有不同...

    值传递方式:

    1、普通调用,形参与实参为同样的类型

    fun(int i)()

    fun(Student stud)

    ()

    main()

    {  

    fun(1);

    Student s1;  

    fun1(s1);

    }

    在函数中可以修改形参的值,但不能改变其对应的实参的值(形参的值变化无法传送到实参) 原因:在函数调用时将建立一个新的对象,是实参对象的拷贝,实参与形参分别占有不同的存储空间,无论形参是否修改都不会改变实参的值。 这种形式的虚实结合,会产生实参的拷贝。当传递的对象较大时,时间开销和空间开销都比较大。

    2、使用指针作为函数参数

    fun(int * i)

    {}

    fun(Student * stud)

    {}

    main()

    {  

    int i = 1;  

    fun(&i);  

    Student s1;  

    fun1(&s1);

    }

    其实质也是值传递方式,只是传递的值是一个地址,实参将地址传递给形参,虚实结合后,实参与形参指向同样的地址。 其操作的都是同一个对象。 虚实结合时,也会产生实参的拷贝,为形参分配内存,用来存放指针值(即地址)

    2、引用传递方式:用引用作为函数参数

    fun(int& i){}

    fun(Student& stud){}

    main()

    {

     int i = 1;  

    fun(i);  

    Student s1;

     fun1(s1);

    }

    不会为形参分配存储空间(常称为建立实参的一个拷贝),而是把实参的地址传递给形参(引用名),引用名也指向实参变量



    /******************************************************/

    1、普通调用

    函数声明时fun(Student stud){};

    而在调用时:

    Student s1;  

    fun1(s1);

    /******************************************************/

    2、使用指针作为函数参数

    函数声明时fun(Student * stud){}

    而在调用时:

    Student s1;  

    fun1(&s1);

    /******************************************************/

    3:用引用作为函数参数

    函数声明时:

    fun(Student& stud){}

    而在调用时:

    Student s1;

     fun1(s1);

    /******************************************************/

    我一直不明白:

    3中方式在声明与调用之间,实参与形参的对应关系是什么?

    把红色部分实参与形参写到一起。。中间再加个等号。。原来是这样:

    前提:

    Student s1;

    实参                    形参

    Student stud    = s1    ;

    Student *stud  =  &s1   ;

    Student &stud  = s1    ;

    好吧,You Know...

    看后勿喷!

    /******************************************************/

    转载于:https://www.cnblogs.com/Vulkan/archive/2012/09/21/7530261.html

    展开全文
  • 2.参数传递方式分:传值和传址3.关于何时使用引用参数?4.总结:什么时候用指针和引用(二) 前言 本文章为转载,转载原链接已失效 1.当一个类对象作为实参数传递时,使用值传递和引用传递有什么区别? 比如: ...

    前言

    本文章为转载,转载原链接已失效

    1.当一个类的对象作为实参数传递时,使用值传递和引用传递有什么区别?

    比如: DateType ExampleFun(CString &strFileName,…)与

    DateType ExampleFun(CString strFileName,…)
    解答之前,我们先来看2个基本的概念:形参和实参。
    ->通俗的讲:形参是形式上的参数,实参是实际的参数;
    ->详细的讲:形参只是对实参的一种抽象类型描述,只是声明一个函数(方法)能接受什么类型的实参,而不确定接受的实参具体内容是多少;
    实参就是传递给函数(方法)对应形参的具体内容(值),形参的初始指(内容)由实参决定.形参在函数(方法)结束返回后就被释放了.

    2.参数传递方式分:传值和传址

    1).传值方式,只是将实参的值的拷贝传递给函数(方法),在方法内对形参进行操作,其对象是实参的拷贝,对实参不能造成影响.在方法结束返回后,形参被释放丢弃,实参的内容并不会改变;
    2).传址方式,将实参的地址传递给函数(方法),在方法内对形参进行操作即等于对实参进行相同的操作,在方法结束返回后,形参同样被释放,实参的内容将会是对形参进行操作的结果.
    而传址方式,又可以细分为:引用传递(pass-by-reference) , 指针传递(pass-by-pointer)
    引用其实就是对象的别名,传对象的引用,用于把一个对象的地址作为参数传过去,而不是对象本身。
    这是我们就明白了前面问题的答案:传递引用,避免了一次实参到形参的拷贝,提高了效率。

    3.关于何时使用引用参数?

    (1)使用引用参数的主要原因有:
    程序员能够修改调用函数中的数据对象
    通过传递引用而不是整个数据对象,可以提高运行速度

    (2)对于使用传递的值而不做修改的函数:
    如果数据对象很小,如内置数据对象,则按值传递
    如果数据对象是数组,则使用指针,并将指针声明为指向const的指针
    如果数据对象是较大的结构则使用const指针或const引用,以提高效率,节省复制结构所需的时间和空间
    如果数据对象是类对象则使用const引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因,因此传递类对象的标准方式是按照引用传递。

    (3)对于修改调用函数中数据的函数:
    如果数据对象是内置数据类型则使用指针。
    如果数据对象是数组则只能使用指针
    如果数据对象是结构则使用引用或指针
    如果数据对象是类对象则使用引用

    4.总结:

    在c/c++编程中,到底什么时候该用指针来传递参数呢?总结如下:

    1. 需要改变实参的时候, 只能用指针.

    2. 传递大型结构并且"只读"其元素的时候,因为大型结构通过值传递, 需要拷贝其每个元素, 这样效率太低.

    3. 需要遍历数组或频繁引用其元素时, 这样效率比使用下标高.

    4. 动态分配空间时, 必须使用指针.

    5. 传递数组时, 必须使用指针.

    6. 函数返回指针时, 比如fopen

    7. 另外,有时候需要使用二级指针,即指针的指针,例如:

      MemAllocate(char *a){
      a=(char *)malloc(sizeof(char));
      }

    当调用此函数进行内存分配时,发现不能分配内存不能成功,因为此时对于a来说,形参改变了,但实参并不会改变,他们对应于不同的内存单元。正确的写法应该是:

    MemAllocate(char **a){
    *a=(char *)malloc(sizeof(char));
    

    }

    这样就能够正确地分配内存了。

    指针和引用很多时候效果是一样的,但是引用往往会带来阅读上的困难,因为在使用方法上和形参一样,但 是具备指针的特性。再加上 如果数据对象是类对象则使用const引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因,因此传递const类对象的标准方式是按照引用传递。其他时候用指针较好

    什么时候用指针和引用(二)

    首先,要认识到在任何情况下都不能用指向空值的引用。一个引用必须总是指向某些对象。
    1.如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针
    2.如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。
    3.不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。
    引用的一些规则如下:
    (1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
    (2)不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
    (3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

    展开全文
  • 形参与实参之间传递

    千次阅读 2013-10-09 11:45:20
    C语言中,参数的传递方式是“单向值传递”,形参实参变量各自有不同的存储单元,被调用函数中的形参变量值的变化不会影响实参变量值。 举个例子  #include  void swap(int x,int y)  {  int z...
  • 面向对象本质就是:以类的方式组织代码,以对象组织(封装)数据。 抽象 (抽象就是抽取像一部分给抽出来变成类) 三大特性: 封装 把数据包装起来 继承 子类 继承父类 子继承父所有东西 多态 同一个事物有...
  • 函数形参与实参

    2019-09-26 10:50:56
    定义:实参与形参的位置依次对应。 序列传参 定义:实参用*将序列拆解后与形参的位置依次对应。 关键字传参 定义:实参根据形参的名字进行对应。 字典关键字传参 定义:实参用**将字典拆解后与形参的名字...
  • 以数组名作函数参数时,实参数组与形参数组都不必定义长度,因此实参与形参的结合方式是地址结合,与数组长度无关。请问这句话的说法是正确的吗 不正确数组名通过传递参数的形式传递到函数后,会自动退化为...
  • 传递方式: ...在函数中可以修改形参的值,但不能改变其对应的实参的值(形参的值变化无法传送到实参) 原因:在函数调用时将建立一个新的对象,是实参对象的拷贝,实参与形参分别占有不同的存储...
  • 一开始没觉得这部分还需要整理一下,仔细看过书之后发现还有点意思,...这是用一个变量去给另一个变量赋值代码,经过这种赋值后b可以随便改变自己值,但是不影响a值,我想说是:函数传递方式的本质就是变量
  • 2.形参的类型决定了形参和实参交互的方式。 (引用->绑定,非引用->拷贝) 3.实参分为:被引用传递(引用形参是实参的别名),被值传递实参形参是两个相互独立的对象)。 4.传值参数:函数对形参做的所有...
  • 形参与实参

    千次阅读 2012-11-19 23:58:03
    实参就是传递给函数(方法)对应形参的具体内容(值),形参的初始指(内容)由实参决定.形参在函数(方法)结束返回后就被释放了. 到这里,就又出现程序设计语言中的另一通用概念参数传递方式,传值和传址; 1.传值方式,...
  • java中方法参数传递方式是按值传递。 如果参数是基本类型,传递是基本类型【字面量值拷贝】。 如果参数是引用类型,传递是该参量所引用对象在堆中【地址值拷贝】。 参考: Java 值传递 or 引用...
  • 传地址值(传引用):是真正以地址的方式在传参数,这是形参实参是同一个对象,才能真正操作实参! 主调函数中可以直接改变变量值,用函数后只是传值要传地址值操作! 主函数中传值理解: int *p1,*p2...
  • 个人认为:这里简单变量应该是指内置类型,内置类型数据在传入函数时,使用值传递方式的效率要高于引用传递和指针传递,因此编译器会采用效率最高方式来实现参数传递,所以选B(当然也允许用户更改),事实上...
  • 定义:实参与形参的位置依次对应。 序列传参 定义:实参用*将序列拆解后与形参的位置依次对应。 关键字传参 定义:实参根据形参的名字进行对应。 字典关键字传参 1.定义:实参用**将字典拆解后与形参的名字进行对应...
  • 封装性 Java封装性就是把对象属性和对属性操作结合成一个独立单位,并尽可能隐藏内部细节...2)信息隐蔽,即尽可能隐蔽对象内部细节,对外形成一个边界,只保留有限对外公开接口使之外部发生联系,这一
  • JS形参与实参问题

    2017-09-07 11:54:00
    JavaScript参数传递也都是采用值传递的方式进行传值。   (1) 通过实参调用函数时候,传入函数里实参的副本而不是实参,因此在函数里面修改参数值并不会对实参造成影响。 例如:将全局变量作为参数传到...
  • 目标: 指针理解 变量生存周期及作用范围 良好动态内存申请和释放 程序内存分配 一个由C/C++编译程序占用内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配...注意它数据结构中堆是...
  • Python3 函数的形参实参

    千次阅读 2019-03-04 22:53:35
    实参的对应关系与形参的对应关系是按位置来依次对应的 说明: 实际参数和形式参数通过位置进行传递的匹配 实参个数必须与形参个数相同2、序列传参: 序列传参是只指函数调用过程中,用*将序列拆解后按照为序列...
  • 函数实参传递的方式有很多:位置实参实参的顺序与形参顺序相同)、关键字实参实参有变量名和值组成)、列表和字典。 1、位置实参 位置实参:基于Python函数调用中每个实参的顺序关联到函数定义中一个形参...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 503
精华内容 201
关键字:

实参与形参的传递方式