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++ 中常說方法中儘量使用指針變量而不是使用變量的原因吧。


圖示

畫了一個小的示意圖,說明下爲啥形參是指針時可以通過拷貝的指針可以改變實參的值;
不是指針時卻不行。

形參爲變量時
形參爲變量指針時


分享下平時思考的筆記,如有不對之處,歡迎指出討論。

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