版權聲明:本文章原創於 RamboPan ,未經允許,請勿轉載。
Java 與 C 方法中實參形參與指針聯繫的分享
因爲不是計算機專業,在大學時也只是簡單地學習了下 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++ 中常說方法中儘量使用指針變量而不是使用變量的原因吧。
圖示
畫了一個小的示意圖,說明下爲啥形參是指針時可以通過拷貝的指針可以改變實參的值;
不是指針時卻不行。
分享下平時思考的筆記,如有不對之處,歡迎指出討論。