2011-02-12 116 views
3

我對C相當陌生,在引用字符串時遇到了數組和指針。我可以要求輸入2個數字(整數),然後返回我想要的數字(第一個數字或第二個數字),沒有任何問題。但是,當我請求名稱並嘗試返回它們時,程序在輸入名字後崩潰,但不知道爲什麼。指針和malloc問題

從理論上講,我希望爲第一個名字保留內存,然後將其擴展爲包含第二個名稱。任何人都可以解釋爲什麼這打破

謝謝!

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



void main() 
{ 
    int NumItems = 0; 

    NumItems += 1; 
    char* NameList = malloc(sizeof(char[10])*NumItems); 
    printf("Please enter name #1: \n"); 
    scanf("%9s", NameList[0]); 
    fpurge(stdin); 

    NumItems += 1; 
    NameList = realloc(NameList,sizeof(char[10])*NumItems); 
    printf("Please enter name #2: \n"); 
    scanf("%9s", NameList[1]); 
    fpurge(stdin); 

    printf("The first name is: %s",NameList[0]); 
    printf("The second name is: %s",NameList[1]); 

    return 0; 

} 
+0

你不能有一個`void`函數返回0;但是`main()`應該總是首先聲明返回`int`。 – 2011-02-12 22:54:15

+0

謝謝喬納森。我做了這個改變。 – Andy 2011-02-12 23:09:56

回答

6

我覺得你的問題是在此代碼:

scanf("%9s", NameList[0]); 

這裏的問題是,scanf要求變量您提供爲存儲結果必然是一個位置一個指針。如果你提供的東西不是指針,scanf會將它看作是一個隨機位置,並將內存寫入隨機位置,導致程序崩潰。

解決這個問題需要兩個步驟。首先,您需要更改您的NameList聲明,以便它不再是char *。原因是一個char *是一個單個字符串,而您想要一個數組字符串。這將被定義爲char **,指向數組char *的指針。這可能看起來像這樣:

char** NameList; 

接下來,您需要爲字符串分配存儲空間。這是棘手的,有點微妙,因爲你必須做兩個分配。首先,你需要爲陣列本身,你可以做這樣的分配空間:

NameList = malloc (sizeof(char*) * NumItems); 

該分配指針字符數組,但它實際上並沒有建立在指針數組的那些點到有效的內存位置。爲了解決這個問題,你會想然後在查看此數組並設置所有元素是指向大到足以容納你的字符串緩衝區 - 在這種情況下,長度爲10的緩衝區:

int i; 
for (i = 0; i < NumItems; ++i) 
    NameList[i] = malloc (10); // Space for 10 characters 

現在,您可以撥打

scanf("%9s", NameList[0]); 

因爲NameList[0]char *指向到其中的角色應該被寫入緩衝區。

對您的代碼有更多評論 - 而不是分配一個元素的數組,然後將它重新分配給兩個元素的數組,然後考慮只是預先分配所有空間。它更清晰一點。另外,由於您現在正在處理一個char *的緩衝區,每個緩衝區都需要初始化以指向其自己的緩衝區,如果您執行增量分配,則需要確保初始化所有新的char *分配指向某處的緩衝區。如果你一次只做這一步,那麼你很可能會忘記設置指針並導致崩潰,而如果你事先做好了,就沒有這種風險。

當需要釋放動態分配的內存時,您需要反向運行分配過程,首先釋放動態分配的字符串緩衝區,然後釋放頂層緩衝區。例如:

for (i = 0; i < NumItems; ++i) 
    free (NameList[i]); 
free (NameList); 

這是必要的,因爲free功能不能遞歸調用。您需要顯式釋放所有分配的內存。

注意,你寫這樣的代碼:

free (NameList); 
for (i = 0; i < NumItems; ++i) 
    free (NameList[i]); 

,因爲如果你先釋放頂層數組這將導致各種壞事在運行時,那麼當你試圖迭代在釋放指針的內容上,你將會閱讀你不再擁有的內存。

希望這會有所幫助!

1

您的NameList變量是一個char *,它是一個指向單個字符串的指針。 (char是單個字符,字符*是一個字符串,字符**是一個字符串數組。)

當使用NameList中[1]時,實際上是索引字符串的第二個字符,不第二個字符串本身。

而應該分配的字符串數組,這樣的事情:

char (*NameList)[10]; 
    NameList = malloc(10*sizeof(char)*NumItems); 

編輯:修正了一些編譯錯誤。 (Sample code.)請注意,sizeof(char)並不是真的需要,因爲它總是1.很高興明確,但。

+0

謝謝,我會給它一個鏡頭。我做的一個快速編輯是將&運算符添加到我的scanf中,它實際上防止它給我一個錯誤,但正如你所說,輸出包括名字的第一個字母,然後是全名。所以當我的意圖直接放在後面時,第二個scanf覆蓋了第一個scanf。 – Andy 2011-02-12 22:48:38

+1

@Eric Pi-我可能是錯的,但這不是非法的代碼? `NameList`是一個數組,你不能爲數組賦值。 – templatetypedef 2011-02-12 22:51:59

1

如果您需要一個2d字符數組(每個元素都是一個char數組)的數組,則您以錯誤的方式分配內存。正確的方法是:

int main(){ 

int i; 
int NumItems = 2; 

/* Alocate a variable which every position points to an array of character */ 
char ** NameList = (char **) malloc(sizeof(char *) * NumItems); 

/* For each position, allocate an array of 10 characters */ 
for(i = 0; i < NumItems; i++){ 
    NameList[i] = (char *) malloc(sizeof(char) * 10); 
} 

printf("Please enter name #1: \n"); 
scanf("%s", NameList[0]); 

printf("Please enter name #2: \n"); 
scanf("%s", NameList[1]); 

printf("The first name is: %s",NameList[0]); 
printf("The second name is: %s",NameList[1]); 

/* Free allocated memory. Always a good practice and prevents memory leaks. */ 
for(i = 0; i < NumItems; i++){ 
    free(NameList[i]); 
} 
free(NameList); 

return 0; 

} 
0

也可以考慮只用棧空間,而不是使用malloc和動態分配的堆空間:

#define NumItems 2 
char NameList[NumItems][10]; 

printf("Please enter name #1: \n"); 
scanf("%9s", NameList[0]); 

printf("Please enter name #2: \n"); 
scanf("%9s", NameList[1]); 

printf("The first name is: %s",NameList[0]); 
printf("The second name is: %s",NameList[0]); 

一般來說,除非你有一個需要動態大小的數組,這是非常非常容易使用普通的數組。