C/C++語言中值傳遞、指針傳遞和引用傳遞

在C/C++語言中值傳遞、指針傳遞和引用傳遞(C++ only)這三種函數參數傳遞方式是比較基本的知識,用的比較普遍,但不仔細分析其實質的話,時間長容易記混了。網上的資料也較多但多數都不繫統,本文力求用最白話的表達和簡單的示例把這三種方式描述清楚。沒時間看分析的直接看簡述就可以了。

簡述

值傳遞就是最普通的傳遞方式,比如函數定義爲fun(int a),在調用的地方有int x=6, 使用fun(x)就可以了。這種方式在fun(int a)函數內部的對a的修改 不能 導致外部x的變化。
指針傳遞其實也就是地址傳遞,函數定義爲fun(int *a),形參爲指針,這就要求調用的時候傳遞進去一個參數的地址,例如int x=6; fun(&x)。 這種方式在fun(int a)函數內部的對a的修改 導致外部x的變化。
引用傳遞只有C++支持,相比前兩種方式用的比較少,但也非常有用。引用傳遞函數定義爲fun(int &a),這裏&符號是引用而不是取地址的意思,調用方式和值傳遞類似,例如int x=6; fun(x)。 但是這種方式在fun(int a)函數內部的對a的修改 導致外部x的變化。

表格是一種比較直接的表達方式,列個表格可以對這三種方式一目瞭然,假設調用fun函數之前都有定義int x=6,這三種方式異同如下表所示:

傳遞方式 函數定義 函數調用 函數內對a修改的影響
值傳遞 fun(int a) fun(x) 外部x不變
指針傳遞 fun(int *a) fun(&x) 外部x同步更改
引用傳遞(C++) fun(int &a) fun(x) 外部x同步更改

分析

下面三部分代碼都嘗試在swap函數內對輸入參數進行數值交換,通過打印其數值和指針來分析。

值傳遞

函數內部使用這個參數,對這個參數的修改對函數外的原始數據不起作用。

示例:

#include <stdio.h>

void swap(int a, int b){
    printf("swap enter\n");
    printf("a = %d, ptr = %p\n", a, &a);
    printf("b = %d, ptr = %p\n", a, &b);

    int tmp = b;
    b = a;
    a = tmp;

    printf("a = %d, ptr = %p\n", a, &a);
    printf("b = %d, ptr = %p\n", b, &b);
    printf("swap leave\n");
}

int main() {
    int x = 1;
    int y = 2;
    printf("x = %d, ptr = %p\n", x, &x);
    printf("y = %d, ptr = %p\n", y, &y);

    swap(x, y);

    printf("x = %d, ptr = %p\n", x, &x);
    printf("y = %d, ptr = %p\n", y, &y);

    return 0;
}

運行結果:

x = 1, ptr = 000000000022FE4C
y = 2, ptr = 000000000022FE48
swap enter
a = 1, ptr = 000000000022FE20
b = 1, ptr = 000000000022FE28
a = 2, ptr = 000000000022FE20
b = 1, ptr = 000000000022FE28
swap leave
x = 1, ptr = 000000000022FE4C
y = 2, ptr = 000000000022FE48

結果表明:
swap函數內部a b的數值進行交換,但是沒有影響到x y的值。swap函數形參a b所指向的內存地址和外部x y的地址不一樣。
實質:
swap爲形式參數 a b創建了內存空間,main函數調用swap函數的時候,把x y的值copy給新創建的 a ba bx y分別在不同的內存位置中,因而對這個新創建的 a b 操作不會影響外部的 x y的值。

帶有返回值的函數,比如int fun(){int x=7; return x},外部調用方式爲int a=fun();,這時是把x的數值copy了一份給外部a所在的內存空間,並釋放了x所在的內存空間(系統自動分配的棧內存),在fun函數調用後即使知道x的內存地址也可能取不到相應的數據。
返回值爲指針的函數,比如char *fun(){...; return str;},函數中待返回的變量必須是使用malloc等函數手動申請空間(堆內存)的數據,且外部使用後要手動釋放。因爲系統自動申請的棧內存會在函數調用後自動釋放。

指針傳遞(地址傳遞)

形參爲指向實參地址的指針,當對形參的指向操作時,就相當於操作實參本身。

示例:

#include <stdio.h>

void swap(int *a, int *b){
    printf("swap enter\n");
    printf("a = %d, ptr = %p\n", *a, a);
    printf("b = %d, ptr = %p\n", *a, b);

    int tmp = *b;
    *b = *a;
    *a = tmp;

    printf("a = %d, ptr = %p\n", *a, a);
    printf("b = %d, ptr = %p\n", *b, b);
    printf("swap leave\n");
}

int main() {
    int x = 1;
    int y = 2;
    printf("x = %d, ptr = %p\n", x, &x);
    printf("y = %d, ptr = %p\n", y, &y);

    swap(&x, &y);

    printf("x = %d, ptr = %p\n", x, &x);
    printf("y = %d, ptr = %p\n", y, &y);

    return 0;
}

運行結果:

x = 1, ptr = 000000000022FE4C
y = 2, ptr = 000000000022FE48
swap enter
a = 1, ptr = 000000000022FE4C
b = 1, ptr = 000000000022FE48
a = 2, ptr = 000000000022FE4C
b = 1, ptr = 000000000022FE48
swap leave
x = 2, ptr = 000000000022FE4C
y = 1, ptr = 000000000022FE48

結果表明:
swap函數內部a b的數值進行交換,外部的x y的值也發生了交換。swap函數形參a b所指向的內存地址和外部x y的地址分別都相同。
實質是:
swap函數形式參數其實是一個指針,可能把fun(int *a)寫成fun(int* a)就比較容易理解了,形參a爲地址指針。*a就是xa就是&x。函數內部要想修改其數值就要使用*a取數值,打印其地址直接打印a就可以了。
函數內部對a b的操作實際上是操作x y所在的內存空間,所以函數內的修改會影響到外部x y的數值。

引用傳遞

引用傳遞的形參加一個&符號,這個形參相當於實參的一個別名,對形參的操作都相當於對實參的操作。
注意形參帶&符號的引用傳遞在C語言中是不可用的,只有C++中支持。
C語言中函數參數總是通過值傳遞,可以通過顯式傳遞指針值模擬引用傳遞。

Function parameters are always passed by value. Pass-by-reference is simulated in C by explicitly passing pointer values. – https://en.wikipedia.org/wiki/C_(programming_language)

示例:

#include <stdio.h>

void swap(int &a, int &b){
    printf("swap enter\n");
    printf("a = %d, ptr = %p\n", a, &a);
    printf("b = %d, ptr = %p\n", a, &b);

    int tmp = b;
    b = a;
    a = tmp;

    printf("a = %d, ptr = %p\n", a, &a);
    printf("b = %d, ptr = %p\n", b, &b);
    printf("swap leave\n");
}

int main() {
    int x = 1;
    int y = 2;
    printf("x = %d, ptr = %p\n", x, &x);
    printf("y = %d, ptr = %p\n", y, &y);

    swap(x, y);

    printf("x = %d, ptr = %p\n", x, &x);
    printf("y = %d, ptr = %p\n", y, &y);

    return 0;
}

運行結果:

x = 1, ptr = 000000000022FE4C
y = 2, ptr = 000000000022FE48
swap enter
a = 1, ptr = 000000000022FE4C
b = 1, ptr = 000000000022FE48
a = 2, ptr = 000000000022FE4C
b = 1, ptr = 000000000022FE48
swap leave
x = 2, ptr = 000000000022FE4C
y = 1, ptr = 000000000022FE48

結果表明:
函數內對形參的使用方式和值傳遞相同,產生的效果和指針傳遞相同。
swap函數內部a b的數值進行交換,外部的x y的值也發生了交換。swap函數形參a b所指向的內存地址和外部x y的地址分別都相同。

實質是:
形式參數前加&符號,這個形參相當於實參的一個別名,對這個形參的操作等同於對實參的操作,有點類似快捷方式。a等同於x,比如fun(int &a)函數內對a的操作和值傳遞的函數fun(int a)中對形參的操作一樣,但是修改形參同時也是修改實參。

發佈了39 篇原創文章 · 獲贊 70 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章