天天看點

struts int參數沒法傳string_C語言函數參數的傳值和傳指針有什麼差別?

作者:守望,Linux應用開發者,目前在公衆号【程式設計珠玑】 分享Linux/C/C++/資料結構與算法/工具等原創技術文章和學習資源。

前言

我們可能聽過C語言中的傳值和傳指針,在其他語言中,也有傳引用一說,那麼他們到底有什麼差別呢?如果你還不能準确地分辨,就該好好了解一下了。

傳值

我們在初學C語言的時候就被老師教過,下面的方式是無法交換a和b的值的:

#include
void swap(int a,int b){
    int temp = a;
    a = b;
    b = temp;
    printf("swap a = %d,b = %d
",a,b);
}
int main(void){
    int a = 10;
    int b = 20;
    printf("before swap:a = %d,b = %d
",a,b);
    swap(a,b);
    printf("after  swap:a = %d,b = %d
",a,b);
    return 0;
}
           

運作結果如下:

before swap:a = 10,b = 20                                                                                                                                                                   
internal swap a = 20,b = 10                                                                                                                                                                 
after  swap:a = 10,b = 20 
           

可以看到,a和b的值最終并沒有被交換。開始時a,b的值為10,20,而最終還是同樣的值。

為什麼呢?因為函數參數在傳遞的時候,都是傳原資料的副本,也就是說,swap内部使用的a和b隻是最初始a和b的一個副本而已,是以無論在swap函數内部對a和b做任何改變,都不會影響初始的a和b的值。

正因如此,我們常常被告知,不要把直接把結構體直接作為參數,這樣效率會很低。由于結構體本身占用位元組數較大,如果直接作為參數,那麼将會産生一個較大的”副本“,如此一來,效率也就很低了。

我們再結合下面的圖來了解:

struts int參數沒法傳string_C語言函數參數的傳值和傳指針有什麼差別?

值傳遞

首先圖中方框中的上部分a和b代表了main函數中的a和b,即原始資料,而方框中的下部分a和b代表了函數的參數a和b,即原始資料的“副本”。(後面的圖都是如此,上部分代表原始值,下部分代表函數參數值)。

調用swap函數前後的情形如下:

struts int參數沒法傳string_C語言函數參數的傳值和傳指針有什麼差別?

調用swap前後

由于在swap中永遠隻是對a和b的副本進行操作,是以完全不影響原始的a和b的值。最終也不可能達到交換a和b的值的目的。

傳指針

那麼為解決上面的問題,我們知道,需要傳指針。其代碼如下:

#include
void swap(int *a,int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
    printf("swap a = %d,b = %d
",*a,*b);
}
int main(void){
    int a = 10;
    int b = 20;
    printf("before swap:a = %d,b = %d
",a,b);
    swap(&a,&b);
    printf("after  swap:a = %d,b = %d
",a,b);
    return 0;
}
           

運作結果:

before swap:a = 10,b = 20                                                                                                                                                                   
swap a = 20,b = 10                                                                                                                                                                          
after  swap:a = 20,b = 10 
           

可以看到在這種情況下,a,b的值才是真正交換了。

為什麼又有傳值,又有傳指針

看到這裡,不知道你是否會疑惑,為什麼給函數傳遞參數的時候,一會是傳值,一會是傳指針呢?為什麼傳指針就能改變參數的值呢?實際上,C語言裡,參數傳遞都是值傳遞!也就是說,你認為的傳指針也是傳值,隻不過它的值是指針類型罷了。

我們再通過圖來了解前面為什麼傳指針就可以交換a,b的值:

struts int參數沒法傳string_C語言函數參數的傳值和傳指針有什麼差別?

從圖中可以看出,雖然傳遞給函數的是指向a和b的指針的副本,但是它的副本同樣也是指向a和b,是以雖然不能改變指針的指向,但是能改變參數a和b指向的内容,即改變原始a和b的值。

再看傳指針

如果是為指針p申請一段記憶體,下面的代碼能達到目的嗎?

#include
#include
void getMemery(int *p){
    /*申請1024個int大小*/
    p = malloc(sizeof(int)*1024);
    if(NULL == p)
    {
        printf("malloc failed
");
        p = NULL;
    }
}
int main(void){
    int *p = NULL;
    getMemery(p);
    printf("address of p is %p
",p);
    return 0;
}
           

通過前面的内容分析,肯定是達不到預期效果的。

運作結果:

address of p is (nil)
           

這是為什麼呢?我們還是利用前面所知來分析,由于傳遞給getMemory函數的參數都是一個副本,是以函數内的p也是外部p的一個副本,是以即便在函數内部,将p指向了一塊新申請的記憶體,仍然不會改變外面p的值,即p還是指向NULL。

struts int參數沒法傳string_C語言函數參數的傳值和傳指針有什麼差別?

getMemory

如何修改呢?我們需要傳入p的位址,即指向int類型指針的指針。

#include
#include
void getMemery(int **p){
    /*申請1024個int大小*/
    *p = malloc(sizeof(int)*1024);
    if(NULL == *p)
    {
        printf("malloc failed
");
        *p = NULL;
    }
}
int main(void){
    int *p = NULL;
    getMemery(&p);
    printf("address of p is %p
",p);
    free(p);
    p = NULL;
    return 0;
}
           

運作結果如下:

address of p is 0x144f010
           

從運作結果可以看到,p的值被改變了,而不再是初始的NULL。

可配合下面的圖進行了解:

struts int參數沒法傳string_C語言函數參數的傳值和傳指針有什麼差別?

getMemory

總結

本文總結如下:

  • 函數的參數都是原資料的“副本”,是以在函數内無法改變原資料
  • 函數中參數都是傳值,傳指針本質上也是傳值
  • 如果想要改變入參内容,則需要傳該入參的位址(指針和引用都是類似的作用),通過解引用修改其指向的内容
  • 以上結論不限于C語言

思考

  • 如何實作不借助第三個變量,交換兩個整數的值?
  • 結合本文,了解C++/Java中所謂的傳引用

●編号524,輸入編号直達本文

●輸入m擷取文章目錄

C語言與C++程式設計

struts int參數沒法傳string_C語言函數參數的傳值和傳指針有什麼差別?

分享C/C++技術文章

繼續閱讀