如果用戶輸入5
作爲第一個輸入,則程序應繼續掃描5個字符串。如何在C中掃描用戶定義的字符串數量?
我想:
int n;
scanf("%d",&n);
char a[100][10];
for (int i = 0; i < n; i++) {
scanf("%s", &a[i][10]);
}
但只掃描一個字,然後退出。
如果用戶輸入5
作爲第一個輸入,則程序應繼續掃描5個字符串。如何在C中掃描用戶定義的字符串數量?
我想:
int n;
scanf("%d",&n);
char a[100][10];
for (int i = 0; i < n; i++) {
scanf("%s", &a[i][10]);
}
但只掃描一個字,然後退出。
鑑於大部分這裏給出的答案是在一些這樣或那樣的缺陷:
接受的答案,例如,指出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程序中進行良好的練習。分離邏輯和編寫函數將會將此片段轉換爲實際有用的代碼。
如果'fgets'最多讀取一個比參數少一個字符,爲什麼要指定9而不是10? –
@TomFenech:咬,你是絕對正確的。對不起,將編輯...這只是我的偏執,促使我寫這種無意義 –
也許我的回答是沒有錯的。我正在編輯我的措辭以使其更清楚。 –
&a[i][10]
計算爲第i個陣列的11要素的地址a
- 然而,由於你的數組只有十個元素這將最終出界。
您需要第i個陣列的地址。你可以通過a[i]
獲得。
'&a [我] [10]'是超出界限的,但如果不是,它會評估到數組的第十一個元素 –
@EliasVanOotegem謝謝,更正 – corazza
不完全,'a [i]'沒有第十一個你沒有提到OP的代碼('&a [i] [10]')在'i'爲99時正在訪問內存...... –
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
中增加的空格使數字後面的換行符也被吞下。
非常感謝我運行..!;) –
@VarunBehl沒問題,樂意幫忙。 –
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]);
這在ideone.com上的編譯器像c一樣工作,但不是在visual studio中! –
謝謝你的方式! ;) –
更改爲'scanf(「%s」,a [i]);' – Dabo
'a [i] [10]'超出界限,'char a [100] [10];'表示最高'a [i]'的索引是9.鑑於你的'scanf'格式指定了一個字符串,你想在這個問題中傳遞'&a [i]' –
字符串,最大長度可以是10,而n是字符串的數量? ? 我的意思是創建一個矩陣,使[0] [10]是第一個字符串,類似的其他字符串直到n。 糾正我,如果我錯了?? –