2014-03-27 44 views
0

如果用戶輸入5作爲第一個輸入,則程序應繼續掃描5個字符串。如何在C中掃描用戶定義的字符串數量?

我想:

int n; 
scanf("%d",&n); 
char a[100][10]; 

for (int i = 0; i < n; i++) { 
    scanf("%s", &a[i][10]); 
} 

但只掃描一個字,然後退出。

+1

更改爲'scanf(「%s」,a [i]);' – Dabo

+0

'a [i] [10]'超出界限,'char a [100] [10];'表示最高'a [i]'的索引是9.鑑於你的'scanf'格式指定了一個字符串,你想在這個問題中傳遞'&a [i]' –

+0

字符串,最大長度可以是10,而n是字符串的數量? ? 我的意思是創建一個矩陣,使[0] [10]是第一個字符串,類似的其他字符串直到n。 糾正我,如果我錯了?? –

回答

2

鑑於大部分這裏給出的答案是在一些這樣或那樣的缺陷:
接受的答案,例如,指出a[i][10]訪問排在第十字符。這完全是錯誤的。由於數組是零索引,所以這實際上試圖連續訪問char,這可能會超出範圍。
相同的答案顯示瞭如何將二維數組分成行和列。這也是錯誤的。一個數組,無論是2D還是1D,都是內存中的一個連續塊。只有一大堆字符。數組a[100][10]的實際佈局如下所示。我剛剛注意到答案提到了這一點,但仍然是:

|/a[0][0] ===============================\\===============>a[99][9]\| 
|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-| 

連續只有100 * 10個字符。訪問a[1][0],則與訪問a[0][10]或實際a[10]相同。如果i爲99,則出現使用a[i][10]的越界風險,因爲如您所見,數組中的最後一個字符位於a[99][9]。除此之外的所有記憶都是禁止的。
所舉的例子,在行和列的格式,其中輸入是,b和c,則不會是這樣的:

_ _ _ _ _ _ _ _ _ a 
_ _ _ _ _ _ _ _ _ b 
_ _ _ _ _ _ _ _ _ c 

但相反,它會是這樣的:

0 1 2 3 4 5 6 7 8 9 
_ _ _ _ _ _ _ _ _ _ 
a _ _ _ _ _ _ _ _ _ 
b _ _ _ _ _ _ _ _ _ 
c _ _ _ _ _ _ _ _ _ 

@ThoFenech的答案好多了,在這裏使用fgets是有道理的。但是,爲了點我的和跨過T:終止的字符不會被fgets讀取。該函數將讀取至多n-1個字符,其中n是指定的最大長度,如文檔所述:

fgets函數最多從指向的流中讀取的最少一個字符數n通過流入由s指向的數組。換行符(保留)或文件結束後不會讀取其他字符。

但是,fgets停止閱讀,如果,並且只有遇到換行符或EOF。遇到NUL字符後它不會停止。記住這一點,但在你的情況下,它不應該是一個擔心。

然而,當使用fgets時,您可以在前面找到大量代碼片段,用於清理輸入緩衝區,並調用fflush(stdin);。然而,這會調用未定義的行爲,並且您首先讀取緩衝區,直到它首先爲空。湯姆·芬奇將這件事留下了,爲了完整起見,我想我也會在這裏提到它。除此之外,我會說他的答案是準確的。

因此,考慮到這一點,這是我的建議:

破除您的固定大小的緩衝區。 a將包含多少個字符串取決於用戶輸入。這意味着數組本身應該是可變長度的。值得慶幸的是,C(自C99以來)只有你需要的東西:可變長度陣列 - 或簡稱VLA將在這裏更可取。這是我寫的是概念的快速測試/證明:

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

int main (void) 
{ 
    int i, n; 
    puts("How many words do you wish to enter?"); 
    scanf(" %d", &n);//get size of input array 
    while((i=getchar()) != EOF && i != '\n');//"flush" stdin 
    char a[n][10]; 
    printf("Please enter %d words now\n", n); 
    for (i=0;i<n;++i) 
     fgets(a[i], 9, stdin);//get 9 char input, not 10 (10th char is nul terminator) 
    for (i=0;i<n;++i) 
     printf("Line %d: %s\n", i+1, a[i]);//print each input line 
    return 0;//return 
} 

您可以使用gcc一個* NIX系統上編譯如下:

$ gcc -std=c99 your_code.c -o progName 
$ ./progName 

並運行它。它按預期工作。
請注意,每個條目的輸入緩衝區只有10個字符長。這意味着一個字如:「成功」是一個no-go(它的長度爲10個字符,但字符串也需要一個NUL終止字符)。如果我是你,我會選擇爲實際輸入使用更大的緩衝區。
由於我們使用VLA,默認情況下,我們不再使用1000個字節的堆棧空間(a[100][10]使用了這麼多)。如果n的值是10,那麼我們可以使我們的緩衝區100字符大,並且最終仍然不會使用比現在更多的內存。因此,或許可以考慮使用:

char a[n][24]; 
for (i=0;i<n;++i) 
    fgets(a[i], 24, stdin); 

理想情況下,你會在每次循環的fgets電話後清潔stdin緩衝區,太:

int j;//for clearing the buffer 
for (i=0;i<n;++i) 
{ 
    fgets(a[i], 24, stdin); 
    if (strlen(a[i]) >= 23)//buffer overflow averted, clear stdin 
     while ((j = getchar()) != EOF && j != '\n');//clear buffer 
} 

如果你不這樣做,你離開緩衝液10,你可能會遇到這樣的事情:

Please enter 2 words now 
successful? 
Line 1: successfu 
Line 2: l? 

完整的代碼,現在看起來是這樣的:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> //add this 

int main (void) 
{ 
    int i, j, n;//extra temp var 
    puts("How many words do you wish to enter?"); 
    scanf(" %d", &n); 
    while((j=getchar()) != EOF && j != '\n'); 
    char a[n][10]; 
    printf("Please enter %d words now\n", n); 
    for (i=0;i<n;++i) 
    { 
     fgets(a[i], 10, stdin); 
     if (strlen(a[i] >= 9)//<-- added 
      while ((j=getchar()) != EOF && j != '\n');//added 
    } 
    for (i=0;i<n;++i) 
     printf("Line %d: %s\n", i+1, a[i]); 
    return 0;//return 
} 

雖然程序編譯和運行方式完全一樣。
如果你想以適應更大的緩衝區,如果用戶只想通過,也就是說,2個字作爲輸入,您可以使用VLA對那個,太:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> //add this 

//max buffer total 1000 chars is what you are using with a[100][10] 
#define MAX_TOTAL_BUFFER 1000 
//don't allow more than 80 chars per entry 
#define MAX_SINGLE_BUFFER 80 
//added for safety 
#define MIN_SINGLE_BUFFER 10 

int main (void) 
{ 
    int i, j, n. buf_len; 
    puts("How many words do you wish to enter?"); 
    scanf(" %d", &n); 
    buf_len = MAX_TOTAL_BUFFER/n; // int/int yields int 1000/3 == 333 
    if (buf_len > MAX_SINGLE_BUFFER) 
     buf_len = MAX_SINGLE_BUFFER; 
    else if (buf_len < MIN_SINGLE_BUFFER) //mind you, risk of stack overflow here 
     buf_len = MIN_SINGLE_BUFFER; 
    while((j=getchar()) != EOF && j != '\n'); 
    char a[n][buf_len]; 
    printf("Please enter %d words (max %d chars long)\n", n, buf_len-1); 
    for (i=0;i<n;++i) 
    { 
     fgets(a[i], buf_len, stdin); 
     if (strlen(a[i] >= buf_len)//<-- added 
      while ((j=getchar()) != EOF && j != '\n');//added 
    } 
    for (i=0;i<n;++i) 
     printf("Line %d: %s\n", i+1, a[i]); 
    return 0;//return 
} 

玩的最大值和最小值的緩衝區,並看看你能推多遠,直到你的堆棧最終溢出。從用戶處獲得後,再檢查n的值。如果用戶通過0,該程序不能正確處理。再次提示用戶輸入正數n,使用默認值或退出。
如果n爲負數,則可以使用其絕對值(或乘以-1),再次提示或退出......所有這些都可以在交互式CLI C程序中進行良好的練習。分離邏輯和編寫函數將會將此片段轉換爲實際有用的代碼。

+0

如果'fgets'最多讀取一個比參數少一個字符,爲什麼要指定9而不是10? –

+0

@TomFenech:咬,你是絕對正確的。對不起,將編輯...這只是我的偏執,促使我寫這種無意義 –

+0

也許我的回答是沒有錯的。我正在編輯我的措辭以使其更清楚。 –

1

&a[i][10]計算爲第i個陣列的11要素的地址a- 然而,由於你的數組只有十個元素這將最終出界。

您需要第i個陣列的地址。你可以通過a[i]獲得。

+0

'&a [我] [10]'是超出界限的,但如果不是,它會評估到數組的第十一個元素 –

+0

@EliasVanOotegem謝謝,更正 – corazza

+0

不完全,'a [i]'沒有第十一個你沒有提到OP的代碼('&a [i] [10]')在'i'爲99時正在訪問內存...... –

1

scanf在這種情況下是危險的,因爲可以輸入比您的數組長度更長的字符串。我會建議使用fgets

#include <stdio.h> 

int main() 
{ 
    int n; 
    scanf("%d ",&n); // note the added space 
    char a[100][10]; 
    for (int i = 0; i < n; i++) { 
     fgets(a[i], 10, stdin); 
    } 
    return 0; 
} 

中間參數fgets指定要讀取的字符的最大數量。 fgets將讀取max - 1個字符,並在字符串的末尾添加一個\0(空字節)。使用這種方法可以防止緩衝區溢出,並確保最終生成一個有效的字符串。

scanf中增加的空格使數字後面的換行符也被吞下。

+0

非常感謝我運行..!;) –

+0

@VarunBehl沒問題,樂意幫忙。 –

0
char a[100][10]; 
_ _ _ _ _ _ _ _ _ _ 
_ _ _ _ _ _ _ _ _ _ 
_ _ _ _ _ _ _ _ _ _ 
     | 
     | 
    100 rOWS and 10 columns 
     | 
     | 
_ _ _ _ _ _ _ _ _ _ 
_ _ _ _ _ _ _ _ _ _ 
_ _ _ _ _ _ _ _ _ _ 

當你做scanf("%s", &a[i][10]);,你是從標準只是每行的字符tenth服用輸入。

例如,如果n=3和輸入是a,b,c 那麼它將在邏輯上看起來像這樣(內存是連續的,但只是爲了理解我使用的是行和列)。

_ _ _ _ _ _ _ _ _ a 
_ _ _ _ _ _ _ _ _ b 
_ _ _ _ _ _ _ _ _ c 

所以要輸入一個完整的字符串,你應該只給出行的基地址。

scanf("%s", &a[i]); 
+0

這在ideone.com上的編譯器像c一樣工作,但不是在visual studio中! –

+0

謝謝你的方式! ;) –