概要:
教C語言課的過程中,發現很多學生對函數調用中的參數傳遞問題很容易糊塗。跟師弟交流的過程中,也發現大家對這個問題理解不夠透徹。因此,結合本人自己的理解,將函數調用中的傳值與傳引用進行分析總結。
一、函數調用中傳遞參數的類型
傳值(pass by value):即形參接收實參傳遞過來的值,如果是自定義類型,要調用拷貝構造函數。函數處理的是形參的值。
傳引用(pass by reference):引用表示與原對象完全相同,是同一個對象。若函數的形參是引用,則實參與形參間不存在參數傳遞,且函數內對形參的修改就是修改實參,因為它們是引用關系,是同一個對象。
1、C語言中,傳遞的參數類型只有1個:傳值,包括傳遞普通數值和指針。
2、C++中,傳遞的參數類型有2個:傳值、傳引用
3、java中,傳遞的類型只有1個:傳值。
java中傳的值包括:基本數據類型和對象,其中對象當做指針看待
三種語言的處理方法相同: 不管傳遞的是普通變量還是指針,都是傳值。對於指針,看函數修改的是指針的值,還是指針所指對象的值就可以了。
二、程序示例
1、C++:傳遞的是基本數據類型,包括普通數值、指針和引用

#include <iostream> using namespace std; void swap(int a,int b); void swapPoint(int *pa, int *pb); void swapData(int *pa,int *pb); void swapReference(int &a, int &b); int main() { int a = 10; int b = 20; cout<<"main函數中, a= "<<a<<" b= "<<b<<endl; swap(a,b); swapPoint(&a,&b); swapData(&a,&b); swapReference(a,b); cout<<"main函數中, a= "<<a<<" b= "<<b<<endl; return 1; } void swap(int a,int b) { int tmp; tmp = a; a = b; b = tmp; cout<<"swap函數中, a= "<<a<<" b= "<<b<<endl; } void swapPoint(int *pa, int *pb) { int *tmp; tmp = pa; pa = pb; pb = tmp; cout<<"swapPoint函數中, a= "<<*pa<<" b= "<<*pb<<endl; } void swapData(int *pa,int *pb) { int tmp; tmp = *pa; *pa = *pb; *pb = tmp; cout<<"swapData函數中, a= "<<*pa<<" b= "<<*pb<<endl; } void swapReference(int &a, int &b) { int tmp; tmp = a; a = b; b = tmp; cout<<"swapReference函數中, a= "<<a<<" b= "<<b<<endl; }
分析:
void swap(int a,int b)函數,形參a、b分別接受實參傳遞的值,函數處理的是形參a、b,實現交換形參a、b的值
void swapPoint(int *pa, int *pb)函數,交換的是指針的值,即交換的是形參的指向關系
void swapData(int *pa,int *pb)函數,交換的是指針所指向的對象,即交換的是實參
2、C++:傳遞的是自定義類型:結構體或者類

class Test { public: int a; Test() { cout<<"Test() 無參構造函數!"<<endl; } Test(int data) { a = data; cout<<"Test(int data) 有參構造函數!"<<endl; } Test(const Test &tmp) { a = tmp.a; cout<<"拷貝構造函數!!"<<endl; } }; void swapClass(Test a, Test b) { Test tmp; tmp = a; a= b; b = tmp; } void swapClassReference(Test &a, Test &b) { Test tmp; tmp = a; a= b; b = tmp; } void swapClassPoint(Test *pa, Test *pb) { Test tmp; tmp = *pa; *pa= *pb; *pb = tmp; } int main() { Test a(10); Test b = 20; swapClass(a,b); // swapClassReference(a,b); swapClassPoint(&a,&b); cout<<a.a<<" "<<b.a<<endl; return 1; }
分析:swapClassReference(Test &a, Test &b)函數,由於是引用,不會出現拷貝構造函數的調用。形參就是實參
swapClass(Test a, Test b)函數,會調用拷貝構造函數,給a,b分配存儲空間。函數處理的是新定義的形參變量a、b
3、java中的參數傳遞:傳值
java中出現對象時,把當做指針看待。例如定義了一個類Test,接下來有定義Test tmp ; //類似於c++的指針,沒有調用構造函數
若Test testc = new Test(); //調用默認構造函數

public class Test { public int data; public String name; public Test() { System.out.println("調用了無參的構造方法Test() "); } public Test(int data,String name) { this.data = data; this.name = name; System.out.println("調用了有參的構造方法Test(int data,String name) "); } public Test(Test src) { data = src.data; name = src.name; System.out.println("調用了拷貝的構造方法Test(Test src) "); } public String toString() { return "data= "+data+", name= "+name; } public static void swap(Test a, Test b) { Test tmp ; //類似於c++的指針,沒有調用構造函數 tmp = a; a = b; b = tmp; } public static void modify(Test a) { a.data += 100; a.name +=" is modified!"; } public static void main(String[] args) { Test testA = new Test(2,"testA"); Test testB = new Test(5,"testB"); Test.swap(testA, testB); Test.modify(testA); System.out.println("testA: "+testA.toString()); System.out.println("testB: "+testB.toString()); } }
分析:
主函數中的swap(Test a, Test b)方法,可以證明java中傳遞的值,而不是引用
modify(Test a)方法中,之所以可以修改屬性的值,是因為修改的是指針所指的對象。
總之,java中出現對象時,把當做指針看待
三、總結
1、 C++中傳值(特別是對於自定義類型),會帶來拷貝構造函數執行的開銷,所以執行效率低一點。
傳引用和傳指針,不執行拷貝構造函數,效率會高
2、 當用按值傳遞方式傳遞或返回一個對象時,編譯器會自動調用拷貝構造函數!
3、 當指針作為形參時,最好畫出指針所指向的對象。然后分析修改的是指針的值,還是指針所指的對象。
(完)