Java 与 C 方法中实参形参与指针联系的分享

版权声明:本文章原创于 RamboPan ,未经允许,请勿转载。


因为不是计算机专业,在大学时也只是简单地学习了下 C ,然而在工作中 (Android) 因为项目原因反而接触了很多 C/C++ 的部分,决定补一下这些基础,在最近看 C / C++ 课程,当然有一个很重要的概念就是指针,之前大学因为没有一定工作经验,所以对于指针理解不够透彻,现在重新看一遍感觉又明白了一些,也做个笔记分享下。

因为主要想分享形参与实参的理解,用交换两个变量的值来举例吧。
先说说 Java ,写个片段来测试看看。


Java 形参为基本类型

	
    public static void main(){
        int a = 20;
        int b = 30;
        System.out.println("main a :" + a + " ; b :" + b);
        switchPara(a,b);
        System.out.println("main a :" + a + " ; b :" + b);
    }
	
	//交换这两个变量的值
	private static void switchPara(int a,int b){
		System.out.println("switchPara a :" + a + " ; b :" + b);
		int temp = b;
		b = a;
		a = temp;
		System.out.println("switchPara a :" + a + " ; b :" + b);
	}

main a :20 ; b :30
switchPara a :20 ; b :30
switchPara a :30 ; b :20
main a :20 ; b :30

从结果可以看到在 switchPara() 方法中,参数确实是发生了变化,但是在 main()中打印的日志,的确没有发生变化。常见的解释是在方法中,形参是实参的拷贝,所以在这个方法里,你只是改变了形参值,并没有改变实参的值。所以在 main() 中没有生效,那么我们先暂时记下这个结论。


Java 形参为类对象

接下来测试下如果交换对象中的基本变量,会不会不一样。


	static class Param{
		public int i;
	}

	public static void main(){
		Param p1 = new Param();
		p1.i = 20;
		Param p2 = new Param();
		p2.i = 30;
		System.out.println("switchPara p1 :" + p1.i + " ; p2 :" + p2.i);
		switchPara(p1,p2);
		System.out.println("switchPara p1 :" + p1.i + " ; p2 :" + p2.i);
	}

	//交换这两个变量的值
	private static void switchPara(Param a,Param b){
		System.out.println("switchPara a :" + a.i + " ; b :" + b.i);
		int temp = b.i;
		b.i = a.i;
		a.i = temp;
		System.out.println("switchPara a :" + a.i + " ; b :" + b.i);
	}
	

main p1 :20 ; p2 :30
switchPara a :20 ; b :30
switchPara a :30 ; b :20
main p1 :30 ; p2 :20

是不是发现有点奇怪,为什么之前单独交换基本类型时,没有生效,而放在一个对象当中就可以生效了,仿佛这个形参不是实参的拷贝。这里暂时有点迷惑,我们就先放着,接下来看看 C/C++ 中,测试下这两个例子会怎么样。


C/C++ 形参为基本类型

	
	//交换这两个变量的值
	void switchPara(int a,int b)
	{
		cout << "switchPara a : " << a << " ; b :" << b << endl;
		int temp = b;
		b = a;
		a = temp;
		cout << "switchPara a : " << a << " ; b :" << b << endl;
	}

	void main()
	{
		int a = 20;
		int b = 30;
		cout << "main a : " << a << " ; b :" << b << endl;
		switchPara(a,b);
		cout << "main a : " << a << " ; b :" << b << endl;
	}
	

main a : 20 ; b :30
switchPara a : 20 ; b :30
switchPara a : 30 ; b :20
main a : 20 ; b :30

看结果,这里和 Java 中使用基本类型一样。接下来我们用结构体作为形参,类似 Java 中的对象。


C/C++ 形参为结构体


	struct Param{
		int i;
	};
	
	//交换这两个变量的值
	void switchPara(Param a,Param b){
		cout << "switchPara a : " << a.i << " ; b :" << b.i << endl;
		int temp = b.i;
		b.i = a.i;
		a.i = temp;
		cout << "switchPara a : " << a.i << " ; b :" << b.i << endl;
	}
	
	
	void main()
	{
		Param p1;
		p1.i = 20;
		Param p2;
		p2.i = 30;
		cout << "main a : " << p1.i << " ; b :" << p2.i << endl;
		switchPara(p1,p2);
		cout << "main a : " << p1.i << " ; b :" << p2.i << endl;
	}

main a : 20 ; b :30
switchPara a : 20 ; b :30
switchPara a : 30 ; b :20
main a : 20 ; b :30

这里发现测试结构体的时候和基本类型一样,在 switchPara()中形参的值确实修改了,而实参的值没有改这里是为什么呢 ? 因为这里的形参确实是拷贝,而 Java 中传入对象的时候就不是拷贝了。

当然我们先继续测试下如果需要在形参改变时也改变实参,那么需要怎么做 ?当然需要用到指针,当然这部分用 C/C++ 测试。


C/C++ 形参为基本类型指针

	
	//交换这两个变量的值
	void switchPara(int * a,int * b)
	{
		cout << "switchPara a : " << *a << " ; b :" << *b << endl;
		int temp = *b;
		*b = *a;
		*a = temp;
		cout << "switchPara a : " << *a << " ; b :" << *b << endl;
	}

	void main()
	{
		int a = 20;
		int b = 30;
		cout << "main a : " << a << " ; b :" << b << endl;
		switchPara(&a,&b);
		cout << "main a : " << a << " ; b :" << b << endl;
	}
	

main a : 20 ; b :30
switchPara a : 20 ; b :30
switchPara a : 30 ; b :20
main a : 30 ; b :20

这里可以看到使用指针,在 switchPara() 中形参是基本类型,如果通过形参的修改了值,也是可以改变实参的值。

接下来还是测试结构体指针。


C/C++ 形参为结构体指针


	struct Param{
		int i;
	};
	
	//交换这两个变量的值
	void switchPara(Param * a,Param * b){
		cout << "switchPara a : " << a->i << " ; b :" << b->i << endl;
		int temp = b->i;
		b->i = a->i;
		a->i = temp;
		cout << "switchPara a : " << a->i << " ; b :" << b->i << endl;
	}
	
	
	void main()
	{
		Param p1;
		p1.i = 20;
		Param p2;
		p2.i = 30;
		cout << "main a : " << p1.i << " ; b :" << p2.i << endl;
		switchPara(&p1,&p2);
		cout << "main a : " << p1.i << " ; b :" << p2.i << endl;
	}

main a : 20 ; b :30
switchPara a : 20 ; b :30
switchPara a : 30 ; b :20
main a : 30 ; b :20

当然使用结构体指针时也是可以在 switchPara() 修改对应实参的值。


讨论结果

从上面的测试来看,可以得出结论。

在 C/C++ 中形参只要不是指针类型,那么改变形参,不会改变实参的值。
在 C/C++ 中形参只要是指针类型,那么可以通过形参去改变实参的值。
在 Java 中形参为基本类型,改变形参不改变实参的值。
在 Java 中形参为类对象,改变形参可以改变实参的值。
注意:在测试例子中,不是算改变形参的值,而是通过形参去访问了实参。

那么是不是得出结论:

Java 中使用方法时,如果形参使用基本类型,就是实参的拷贝,而使用类对象时,传递的就是指针。

我们来回忆下 Java 中关于对象的生成与使用。

	
	class Param{
		int i;
	}

	public static void main(){
		Param p1 = new Param();
		p1.i = 20;
	}

如果使用 Java 熟悉点的朋友都知道,我们在

Param p1 = new Param();

这句话中,Param p1 代表声明了一个 Param 类型的引用,而 new Param() 才代表生成了一个 Param 对象;而 p1 这个引用是放在栈中的,而 Param 对象是放在堆中的,实际上我们用 p1.i = 20 时,是使用栈中 p1 去访问了堆中的 Param 对象,并且进行了写操作。

如果我们 main() 中是这样的话,那么肯定会报错。

	
	class Param{
		int i;
	}

	public static void main(){
		Param p1 = null;
		p1.i = 20;
	}

当然大家都知道是什么错误,空指针!是不是也确实说明这个 p1 引用就是一个 Param 类型的指针,如果你不让指针去指向某个对象,那么肯定没法对那个对象进行读写操作。

至于为什么 Java 要考虑在形参使用类对象时,不使用拷贝而使用指针,可能有很多因素,但有一个原因可以确定:

如果使用拷贝的话,会因为执行大量方法而生成很多新的对象,频繁触发 GC ,那么这肯定不合理。

或许也是 C/C++ 中常说方法中尽量使用指针变量而不是使用变量的原因吧。


图示

画了一个小的示意图,说明下为啥形参是指针时可以通过拷贝的指针可以改变实参的值;
不是指针时却不行。

形参为变量时
形参为变量指针时


分享下平时思考的笔记,如有不对之处,欢迎指出讨论。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章