2013-02-22 64 views
0

我試圖編寫一個簡單的shell接口,它接受用戶輸入(通過字符),並通過指針指向一個指針*(確切地說argv是如何工作的)。這裏是我的代碼:令牌化C中的用戶輸入(存儲在** arg中)?

int i = 0; 
for (i = 0; i < wordCount; i++) 
    printf("Word %i: %s\n", i, argvInput[i]); 

argvInput的值[I]是什麼最後輸入分配是所有連接:通過argvInput

char input[100]; 
char **argvInput; 
char ch; 
int charLoop = 0; 
int wordCount = 0; 

argvInput = malloc(25 * sizeof(char *)); 

while((ch = getc(stdin))) { 
    if ((ch == ' ' || ch == '\n') && charLoop != 0) { 
     input[charLoop] = '\0'; 
     argvInput[wordCount] = malloc((charLoop + 1) * sizeof(char)); 
     argvInput[wordCount] = input; 
     charLoop = 0; 
     wordCount++; 

     if (ch == '\n') { 
      break; 
     } 

    } else if (ch != ' ' && ch != '\n') { 
      input[charLoop] = ch; 
      charLoop++; 
     } else { 
      break; 
     } 
    } 

如果我循環。所以,如果我輸入: 「幸福的日子即將到來」,環路的輸出是:

Word 0: soon 
Word 1: soon 
Word 2: soon 
Word 3: soon 
Word 4: soon 

我不知所措。顯然,每個循環覆蓋以前的值,但我盯着屏幕,無法弄清楚爲什麼...

+0

你可能想看看進入['strtok'](http://en.cppreference.com/w/c/string/byte/ strtok)功能。如果您搜索,有很多示例可用。 – 2013-02-22 01:47:30

+0

這最終將成爲一個shell腳本(隨着學期的進展而發展)。由於它最終必須處理管道和報價,因此strtok不會長期工作。 – 2013-02-22 02:31:15

回答

4

此行是你的死穴:

argvInput[wordCount] = input; 

不要緊,你分配新的空間,如果你打算用另一個替換指針(即input)。

相反,使用strncpy來提取input的部分爲argvInput[wordCount]

+3

或者只是使用argvInput [wordCount] = strdup(input); – Clyde 2013-02-22 01:48:05

+0

'strncpy'是背信棄義的,因爲它_may_或_may不會終止目標字符串。跳過分配並做例如'strdup'可能更好(也更簡單)。 – 2013-02-22 01:49:10

+0

@JoachimPileborg:他知道長度,他可以自己終止。事實上,他*是*自己終止它:'input [charLoop] ='\ 0''。所以這是對他的代碼的最小改變,只要他自己分配東西。 – Amadan 2013-02-22 01:51:04

2

argvInput[wordCount] = input;僅使指針argvInput[wordCount]指向input的內存,而不是將輸入的內容複製到新分配的內存中。您應該使用memcpy或strcpy來更正您的程序。

指針分配後的內存狀態如下圖所示。由malloc((charLoop + 1) * sizeof(char));分配的內存(圖中灰色部分)無法再被您的程序訪問,這會導致一些內存泄漏問題。請注意這一點。

enter image description here

0

我建議打印您argvInput指針與%P,而不是%S,找出這個問題:printf("Word %i: %p\n", i, (void *) argvInput[i]);

你注意到它打印出的值是什麼?這與argv的行爲有何不同?嘗試打印argv的指針:for (size_t x = 0; x < argc; x++) { printf("Word %zu: %p\n", x, (void *) argv[x]); }

現在您已經觀察到問題了,解釋它可能會變得更容易。

此代碼分配內存,並且存儲一個指向在argvInput [的wordCount]認爲存儲器:argvInput[wordCount] = malloc((charLoop + 1) * sizeof(char));(順便說一下,焦炭的sizeof是總是 1 C,所以你通過1不必要地相乘)。

這段代碼用一個指向輸入的指針替換指向已分配內存的指針:argvInput[wordCount] = input; ...因此,所有項目都包含一個指向同一個數組的輸入:input,並且由於失去對它的引用而導致分配的內存泄漏。顯然,這是有問題的路線;它不會做你最初認爲它所做的事情。

有人建議您使用strdup調用替換您的malloc調用,並刪除有問題的行。我不喜歡這個建議,因爲strdup不在C標準中,所以不需要存在。

strncpy可以工作,但它不必要的複雜。由於目標數組的大小足以存儲字符串,因此strcpy可以保證正常工作。因此,我建議用strcpy(argvInput[wordCount], input);替換有問題的行。

另一個沒有詳細解釋的選項是strtok。看來這是目前最好的,尚未開發,因爲它需要對代碼進行太多的修改。

我有一塊骨頭可以用此代碼挑選:char ch; ch = getc(stdin);是錯誤的。 getc返回一個int,原因是:任何成功的字符讀取都將以unsigned char值的形式返回,這不可能是負數。如果getc遇到EOF或錯誤,它將返回一個負值。一旦您將返回值分配給ch,您如何區分錯誤和成功?

你有沒有想過如果第一個字符是''會發生什麼?目前,你的代碼會跳出循環。這看起來像一個bug,如果你的代碼是模仿常見的argv解析行爲。適應這個代碼來解決你的問題可能是一個好主意:

for (int c = getc(stdin); c >= 0; c = getc(stdin)) { 
    if (c == '\n') { 
     /* Terminate your argv array and break out of the loop */ 
    } 
    else if (c != ' ') { 
     /* Copy c into input */ 
    } 
    else if (charLoop != 0) { 
     /* Allocate argvInput[wordCount] and copy input into it, 
     * reset charLoop and increment wordCount */ 
    } 
} 
+0

是的,如果第一個字符是空格,它現在會打破循環。這根本不是理想的行爲,但對於這份任務,他告訴我們假設沒有人會放棄第一個角色。無論哪種情況,我都不喜歡我的默認處理方式,但它會隨着時間的推移而變化......並且您的模板非常適合這樣做。 – 2013-02-22 18:58:30

+0

是的,除了有不必要的malloc和strcpys。我在malloc外面添加了一個大緩衝區(比如1024字節),將輸入設置爲指向該緩衝區的起始字節,並增加每次讀取的輸入指針,以便這些字全部存儲在一個緩衝區中,一個之後。當緩衝區已滿時,我會使用realloc將其大小加倍並繼續正常。該模板仍然是相同的,但反映它們的評論和代碼會發生變化。限制每個字99個字節被刪除,整個數據結構將變得更加緩存友好。 – Sebivor 2013-02-23 04:30:46

+0

我不能走那條路。這個想法是一個shell,因此,我發佈的while循環處於永久循環。所以如果我保持加倍的緩衝區,最終它將成爲一個內存豬。我每次malloc的原因是因爲我實際上在每個shell命令運行後釋放內存。 – 2013-02-24 17:33:16