2016-09-14 25 views
10

這個問題,就是要用作此FAQ規範重複:動態存儲器存取只能內部功能

我在函數內部動態分配的數據,一切運作良好,但只是裏面的函數,其中分配發生。當我嘗試在函數外部使用相同的數據時,出現崩潰或其他意外的程序行爲。

這裏是一個MCVE:

#include <stdlib.h> 
#include <stdio.h> 

void print_array (int* data, int size) 
{ 
    for(int i=0; i<size; i++) 
    { 
    printf("%d ", data[i]); 
    } 
    printf("\n"); 
} 


void create_array (int* data, int size) 
{ 
    data = malloc(sizeof(*data) * size); 
    for(int i=0; i<size; i++) 
    { 
    data[i] = i; 
    } 

    print_array(data, size); 
} 


int main (void) 
{ 
    int* data; 
    const int size = 5; 

    create_array(data, size); 
    print_array(data, size); // crash here 
} 

每當print_arraycreate_array函數中調用,我得到預期的輸出0 1 2 3 4,但是當我把它從main,我得到一個程序崩潰。

這是什麼原因?

+3

我幾乎低估了你犯了這樣一個愚蠢的錯誤:) –

+0

@ Jean-FrançoisFabre不幸的是,我還沒有找到一種方法來提出這個問題社區維基,只有答案。我推出了mods,希望它很快會轉化爲社區wiki。 – Lundin

+2

我認爲可以更好地託管在文檔測試版上。 – LPs

回答

10

這個錯誤的原因是create_array函數使用的data是一個局部變量,只存在於該函數中。從malloc獲得的分配的內存地址只存儲在本地變量中,並且永遠不會返回給調用者。


考慮這個簡單的例子:

void func (int x) 
{ 
    x = 1; 
    printf("%d", x); 
} 

... 
int a; 
func(a); 
printf("%d", a); // bad, undefined behavior - the program might crash or print garbage 

這裏,複製可變a的在函數內部本地存儲的,作爲參數x。這被稱爲傳遞值

x被修改時,只有該局部變量被更改。調用者中的變量a保持不變,並且由於a未初始化,所以它將包含「垃圾」並且不能可靠地使用。


指針也不例外,這種傳遞值規則。在你的例子中,指針變量data通過值傳遞給函數。該函數內的data指針是本地副本,從malloc分配的地址永遠不會傳回給調用者。

因此,調用者中的指針變量保持未初始化狀態,因此程序崩潰。另外,create_array函數也創建了一個內存泄漏,因爲在該函數執行之後,程序中不再有任何指針來跟蹤該分配的內存塊。


有兩種方法可以修改函數以按預期工作。無論是局部變量的副本返回給調用者:

int* create_array (int size) 
{ 
    int* data = malloc(sizeof(*data) * size); 
    for(int i=0; i<size; i++) 
    { 
    data[i] = i; 
    } 

    print_array(data, size); 

    return data; 
} 

int main (void) 
{ 
    int* data; 
    const int size = 5; 

    data = create_array(size); 
    print_array(data, size); 
} 

或地址傳遞給調用者的指針變量,並直接寫入到呼叫者變量:

void create_array (int** data, int size) 
{ 
    int* tmp = malloc(sizeof(*tmp) * size); 
    for(int i=0; i<size; i++) 
    { 
    tmp[i] = i; 
    } 

    *data = tmp;  
    print_array(*data, size); 
} 

int main (void) 
{ 
    int* data; 
    const int size = 5; 

    create_array(&data, size); 
    print_array(data, size); 
} 

兩種形式是好的。

+0

非常好的問題和答案。你說指針變量是按值傳遞的,這很有趣,因爲它是一個包含地址的指針。你在第二個函數中犯了一個錯誤,它應該是'print_array(tmp,size); * data = tmp'。或者你可以寫:'* data = tmp; printArray(* data,size); ' –

+0

@ RestlessC0bra謝謝,它已被修復。雖然這是一個社區wiki,所以你也可以自由編輯它。 – Lundin