C/C++中的引數傳遞方式

藍月心語發表於2016-10-12

  版權宣告未經作者允許,嚴禁用於商業出版,否則追究法律責任。網路轉載請註明出處,這是對原創者的起碼的尊重!!!


1 傳值

   C/C++預設的採用傳值,即在函式被呼叫的時候,給形參申請一個空間,再將實參的值傳遞給形參,對形參的任何改變不會影響實引數的值:

#include<iostream>
using namespace std;
int add(int x)
{
    cout << "變化前,形式引數所在地址:" << &x << "   " << "形式引數值:" << x << endl;
    x++;
    cout << "變化後,形式引數所在地址:" << &x << "   " << "形式引數值:" << x << endl;
    return 0;
}
int main()
{
    int a = 1;
    cout << "呼叫前,形式引數所在地址:" << &a << "   " << "形式引數值:" << a << endl;
    add(a);
    cout << "呼叫後,形式引數所在地址:" << &a << "   " << "形式引數值:" << a << endl;
    return 0;
}

   輸出結果如下:
    傳值

  可以看到形參實參的地址不一樣,說明新申請了記憶體空間。
  可以看到對形參的操作不影響實參。
  注:x是在函式內部定義的變數因而其作用範圍是區域性的(僅在該函式範圍內能訪問),生命週期是動態的(呼叫該函式時候建立,函式呼叫完成後就消除。)

2 傳址

2.1 傳指標

  所謂傳址又叫做傳指標,即在函式被呼叫的時候,給形參開闢一個空間用來存放傳遞過來的地址,將實參所在的記憶體地址傳遞給形參,對形參的任何改變也不會影響實參所指的內容,但是對形參所指的內容的改變將會影響到實參所指的內容(因為這兩個指標都指向同一個記憶體空間)

#include<iostream>
using namespace std;
int fun(int *x,int *y)
{
    cout << "變化前,指標x所在的地址:" << &x <<"  指標y所在地址:"<< &y <<endl;
    cout << "變化前,指標x的值      :" << x << "  指標y的值    :" << y<<endl;
    cout << "變化前,指標x所指的值  :" << *x<< "             " <<"  指標y所指的值:"<< *y<<endl;
    x++;
    (*y)++;
    cout << "變化後,指標x所在的地址:" << &x <<"  指標y所在地址:"<<&y<<endl;
    cout << "變化後,指標x的值      :" << x  <<"  指標y的值    :" << y<<endl;
    cout << "變化後,指標x所指的值  :" << *x<< "             " <<"  指標y所指的值:"<< *y<<endl;
    return 0;

}
int main()
{
    int n1 = 1,n2=1;
    int *a = &n1;
    int *b = &n2;
    cout << "呼叫前,指標a所在的地址:" << &a <<"  指標b所在地址:"<<&b<<endl;
    cout << "呼叫前,指標a的值      :" << a  <<"  指標b的值    :" << b<<endl;
    cout << "呼叫前,指標a所指的值  :" << *a<< "             " <<"  指標b所指的值:"<< *b<<endl;
    fun(a,b);
    cout << "呼叫後,指標a所在的地址:" << &a <<"  指標b所å¨地址:"<<&b<<endl;
    cout << "呼叫後,指標a的值      :" << a  <<"  指標b的值    :" << b<<endl;
    cout << "呼叫後,指標a所指的值  :" << *a<< "             " <<"  指標b所指的值:"<< *b<<endl;
    return 0;
}   


  執行下結果如下:
    傳址
  指標a和指標x所在地址不同說明新申請記憶體空間用以存放傳來的地址。
  對指標x的值的改變不會改變指標a的值,也不會改變指標a所指的值,所以輸出結果為1。
  對指標y所指的值的改變會改變指標b所指的值。所以輸出結果為2。

2.2 傳陣列

  傳陣列是傳址的另一方式,陣列作為形參傳遞,實質傳遞的陣列的首地址,一維陣列形參 :資料型別 陣列名[],二維陣列形參:資料型別 陣列名[][上界]
  注意:陣列在傳遞時會退化為指標,因而要傳入陣列長度。

3 傳引用

3.1 傳引用

  引用是什麼?C++中的引用可以理解為typedef的作用(C中是沒有此用法),引用相當於是實參的別名,對形參的任何操作,就是對實參的操作,函式呼叫時不會再記憶體中新開闢空間。

#include<iostream>
using namespace std;
int add(int &x)
{
    cout << "變化前,形式引數所在地址:" << &x << "   " << "形式引數值:" << x << endl;
    x++;
    cout << "變化後,形式引數所在地址:" << &x << "   " << "形式引數值:" << x << endl;
    return 0;
}
int main()
{
    int a = 1;
    cout << "呼叫前,形式引數所在地址:" << &a << "   " << "形式引數值:" << a << endl;
    add(a);
    cout << "呼叫後,形式引數所在地址:" << &a << "   " << "形式引數值:" << a << endl;
    return 0;
}

  執行結果如下:

    傳引用

  可以看到實參和形參的地址是相同的,也就是說並沒有開闢一個新的空間。對引用的操作就是對實參的操作。

3.2 傳指標的引用

  其形式為function(資料型別* &指標名),這裡的指標名就是引用,就是傳來的指標的別名。對它的操作就是對實參指標的操作(類似於C中的二級指標。),對它所指的值的操作,就是對實參指標所指的值的操作。

#include<iostream>
using namespace std;
#define ok 0
int add(int* &x)
{
    cout << "變化前指標x所在的地址: " << &x << endl;
    cout << "變化前指標x的值      : " << x << endl;
    cout << "變化前指標x所指的值  : " << *x << endl;
    x++;
    cout << "變化後指標x所在的地址: " << &x << endl;
    cout << "變化後指標x的值      : " << x << endl;
    cout << "變化後指標x所指的值  : " << *x << endl;
    return ok;
}
int main()
{
    int c = 1;
    int *a = &c;
    cout << "呼叫前指標a所在的地址:" << &a << endl;
    cout << "呼叫前指標a的值      :" << a << endl;
    cout << "呼叫前指標a所指的值  :" << *a << endl;
    add(a);
    cout << "呼叫後指標a所在的地址:" << &a << endl;
    cout << "呼叫後指標a的值      :" << a << endl;
    cout << "呼叫後指標a所指的值  :" << *a << endl;
    return ok;
}

  執行結果如下:

    指標的引用

  可以看到呼叫時沒有在記憶體中開闢空間儲存傳遞來的地址。對形參的改變就是對實參的改變。

4 總結

  一般時候對傳入的實參如果要做改變,或傳入的資料非常大時候,C++中優先考慮傳引用(C/C++都可以傳址,然後對指標所指內容做改變),這樣呼叫函式時可以節約拷貝的時間和空間;如果對傳入的實參不做改變可以傳值、傳引用、傳址。

  傳引用有些時候可以避免未初始化的錯誤。

#include<iostream>
using namespace std;
#define ok 0
struct linknode
{
    int data;
    linknode *next;
};
typedef linknode linklist;
int initlist(linklist *L)
{
    L = (linknode*)malloc(sizeof(linknode));
    return ok;
}
int main()
{
    linklist* L;
    initlist(L);
}

  這裡由於在主函式定義了指標變數L後沒有初始化賦值,因而出現未初始化的錯誤。將int initlist(linklist *L)改成int initlist(linklist *&L)就可以免於此錯誤。

#include<iostream>
using namespace std;
#define ok 0
struct linknode
{
    int data;
    linknode *next;
};
typedef linknode linklist;
int initlist(linklist *L)
{
    L = (linknode*)malloc(sizeof(linknode));
    return ok;
}
int main()
{
    linklist *L;
    cout<<&L
    initlist(L);
}

  版權宣告未經作者允許,嚴禁用於商業出版,否則追究法律責任。網路轉載請註明出處,這是對原創者的起碼的尊重!!!


相關文章