[英]How to scan a user defined number of strings in C?
如果用戶輸入5
作為第一個輸入,則程序應繼續掃描5個字符串。
我試過了:
int n;
scanf("%d",&n);
char a[100][10];
for (int i = 0; i < n; i++) {
scanf("%s", &a[i][10]);
}
但這只會掃描一個單詞然后退出。
考慮到這里給出的大多數答案在某種程度上都有缺陷:
例如,接受的答案指出a[i][10]
訪問行中的第十個字符。 這是完全錯誤的。 由於數組的索引為零,因此實際上嘗試訪問行中的第11個字符,這很可能超出范圍。
相同的答案顯示了如何將2D數組分為行和列。 這也是錯誤的。 數組(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]
風險,因為如您所見,數組中的最后一個char位於a[99][9]
。 超出該限制的所有內存均無法使用。
給定的示例以行和列格式輸入了a,b和c,則該示例看起來不是這樣的:
_ _ _ _ _ _ _ _ _ a
_ _ _ _ _ _ _ _ _ b
_ _ _ _ _ _ _ _ _ c
而是,它看起來像這樣:
0 1 2 3 4 5 6 7 8 9
_ _ _ _ _ _ _ _ _ _
a _ _ _ _ _ _ _ _ _
b _ _ _ _ _ _ _ _ _
c _ _ _ _ _ _ _ _ _
@TomFenech的答案要好得多,在這里使用fgets
很有意義。 但是,要在i上加點,然后在T上加點:nget終止的char不會被fgets讀取。 該函數最多讀取n-1個字符,其中n是指定的最大長度,如文檔所述:
fgets函數從stream指向的流到s指向的數組中讀取的字符數最多少於n指定的字符數。 在換行符(保留)或文件結束之后,不會讀取其他字符。
但是,只有在遇到斷行或EOF時, fgets
才會停止讀取。 遇到NUL字符后,它不會停止。 請記住這一點,但就您而言,不必擔心。
但是,在使用fgets
時,可以找到大量的片段,它們稱為fflush(stdin);
預先清潔輸入緩沖區。 但是,這會調用未定義的行為,您可以很好地讀取緩沖區,直到緩沖區為空為止。 湯姆·芬奇(Tom Fench)忽略了這一點,為了完整起見,我也想在這里提及。 除此之外,我會說他的答案是正確的。
因此,考慮到這一點,這是我的建議:
取消使用固定大小的緩沖區。 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個字符。 這意味着像“成功”這樣的單詞是不可行的(它的長度為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
}
但是,該程序以完全相同的方式編譯和運行。
如果您只想通過兩個字作為輸入,如果您想容納更大的緩沖區,那么您也可以使用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程序中的良好練習提供了幫助。 分離邏輯,並編寫函數將有助於將該代碼段轉換為實際的有用代碼。
&a[i][10]
的計算結果為第i個陣列中的11要素的地址a
-然而,由於你的數組只有十個元素這將最終出界。
您需要第i個數組的地址。 你可以用a[i]
來得到。
在這種情況下, 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
讀取max - 1
字符,並在字符串末尾添加\\0
(空字節)。 使用這種方法可以防止緩沖區溢出,並確保您以有效的字符串結尾。
在scanf
增加的空間是為了使數字后的換行符也被吞下。
char a[100][10];
_ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _
|
|
100 rOWS and 10 columns
|
|
_ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _
當您執行scanf("%s", &a[i][10]);
,您將僅從stdin輸入每行的tenth
字符。
例如,如果n=3
且輸入為a,b,c,則邏輯上將看起來像這樣(內存是連續的,但僅出於理解目的,我正在使用行和列)。
_ _ _ _ _ _ _ _ _ a
_ _ _ _ _ _ _ _ _ b
_ _ _ _ _ _ _ _ _ c
因此,要輸入整個字符串,您應該只給出row的基地址。
scanf("%s", &a[i]);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.