早上好,我遇到了一些 C 代碼的問題,如果輸入的長度小于“猜測”的大小,每次輸入輸入時我都必須按兩次輸入。
如果輸入的長度比猜測的長,我只按一次回車,它就可以正常作業了。
我不確定這里的問題是什么,但我提供了我認為是問題根源的函式以及呼叫者函式和 main 只是為了背景關系。
輸出:
Guess a number: 5555555555
Invalid guess.
Guess a number: 55555555555
Invalid guess.
Guess a number: 555
Invalid guess.
Guess a number:
Invalid guess.
Guess a number: 5555
當我洗掉該行時:
while((ch = getchar()) != '\n' && ch != EOF); // Flush the input buffer
并且我超出了緩沖區的大小,我收到了這個輸出:
Guess a number: 5555555555555555555555555555555555
Invalid guess.
Guess a number: Invalid guess.
Guess a number: Invalid guess.
Guess a number: Invalid guess.
Guess a number: Invalid guess.
問題中的功能
char * get_input(char * guess)
{
print_message("guess"); // Prompt user to input a guess
fgets(guess, sizeof (guess), stdin);
if(feof(stdin))
{
printf("error");
exit(EXIT_FAILURE);
}
int ch = 0;
while((ch = getchar()) != '\n' && ch != EOF); // Flush the input buffer
guess[strlen(guess)-1] = '\0'; // Erase new line character
return guess;
}
呼叫函式
int make_guess(int *v_guess_count)
{
int result = 0;
bool valid = false;
char guess[10] = {'\0'}; // Buffer to store the guess
do
{
get_input(guess); // Get the input
if(is_valid(guess)) // Check if the input is valid
{
valid = true;
*v_guess_count = 1;
}
}
while (! valid); // Keep asking for input until guess is valid
result = assign_value(guess); // Assign the guess
return result;
}
主要的
int main(int argc, char * argv[])
{
int red = 0;
int white = 0;
int v_guess_count = 0;
int target = get_target();
bool game_won = false;
while(game_won == false)
{
red, white = 0; // Reset to zero after each guess
int guess = make_guess(&v_guess_count); // Make a guess. If it's valid, assign it.
printf("guess is: %d\n", guess);
compare(guess, target, &red, &white); // Check the guess with the target number.
print_hints(&red, &white);
if (red == 4)
{
game_won = true;
}
}
printf("You win! It took you %d guesses.\n", v_guess_count);
return 0;
}
uj5u.com熱心網友回復:
你有兩個有點相關的問題。
一。在你的功能
char * get_input(char * guess)
你的線
fgets(guess, sizeof (guess), stdin);
不做你認為它做的事。您想知道fgets緩沖區有多大,即guessfor指向fgets要讀取的記憶體量。但是在函式中get_input,引數guess是一個指標,因此sizeof(guess)將是該指標的大小,而不是它指向的陣列的大小。也就是說,您將獲得可能是 4 或 8 的大小,而不是陣列guess中make_guess宣告的 10。
要解決此問題,請將您的輸入功能更改為
char * get_input(char * guess, int guess_size)
并將呼叫更改make_guess為
get_input(guess, sizeof(guess));
有關這一點的更多資訊,請參閱此問題以及此答案。
二。您用于讀取用戶猜測的陣列guess太小。與其將其設為 10 號,不如將其設為 500 號或其他大小。這樣它就“永遠”不會溢位。不要擔心這樣做會浪費記憶體——記憶體很便宜。
使輸入緩沖區變大的原因是:如果使緩沖區變小,則必須擔心用戶可能鍵入的行太長而fgets無法讀取所有內容的情況。另一方面,如果您使緩沖區變大,則可以宣告問題“不會發生”,因此您不必擔心。而你不想擔心它的原因是擔心它很難,并且會導致像你在這里遇到的問題。
嚴格正確使用fgets,同時擔心用戶輸入溢位緩沖區的可能性,意味著檢測到它發生了。如果fgets沒有讀取所有輸入,則意味著它仍然位于輸入流上,等待混淆程式的其余部分。在這種情況下,是的,您必須閱讀或“重繪 ”或丟棄它。這就是你的線
while((ch = getchar()) != '\n' && ch != EOF);
嘗試去做——但關鍵是只有在fgets遇到不夠大的問題時才需要這樣做。如果fgets沒有問題 - 如果緩沖區足夠大 - 你不想做重繪 輸入的事情,因為它會吞噬用戶的下一行預期輸入,正如你所發現的.
現在,說了這么多,我不得不提醒你。一般來說,“讓你的陣列變大,這樣你就不必擔心它們不夠大”的策略不是一個好的策略。在一般情況下,由于緩沖區溢位,該策略會導致不安全的程式和可怕的安全問題。
不過,在這種情況下,問題還算不錯。 fgets將盡最大努力不要向目標陣列寫入超過目標陣列可以容納的內容。(如果fgets你正確地傳遞了大小,也就是說,如果你解決了問題,這將做得很好——避免緩沖區溢位的完美作業。)如果緩沖區不夠大,最壞的問題是輸入行的過長部分將留在輸入流上并被讀取購買稍后的輸入功能,從而使事情變得混亂。
因此,您總是想考慮特殊情況,并考慮您的程式在所有情況下將要做什么,而不僅僅是“好”的情況。對于“真正的”程式,您必須努力使行為在所有情況下都是正確的。不過,對于像這樣的初級程式,我想大多數人都會同意只使用一個巨大的緩沖區就可以了,然后就完成了。
如果您想獲得額外的功勞,并完美地處理用戶輸入的內容超過fgets輸入緩沖區的情況,您首先必須檢測到這種情況。代碼看起來像:
if(fgets(guess, guess_size, stdin) == NULL)
{
printf("error");
exit(EXIT_FAILURE);
}
if(guess[strlen(guess)-1] != '\n')
{
/* buffer wasn't big enough */
int ch = 0;
while((ch = getchar()) != '\n' && ch != EOF); // Flush the input buffer
/* now print an error message or something, */
/* and ask the user to try again with shorter input */
}
但關鍵是你只在沒有讀完整行while((ch = getchar()) != '\n' && ch != EOF)的情況下才做這件事,而不是在它成功的情況下。fgets
如果你還在我身邊,這里有兩個比較重要的腳注。
我建議更改您的
get_input函式以采用第二個引數int guess_size,但事實證明,對于事物的大小使用更好的型別是size_t,因此更好的宣告將是size_t guess_size。我建議進行測驗
if(guess[strlen(guess)-1] != '\n')以檢測fgets無法讀取整行,但在fgets以某種方式回傳空行的晦澀情況下,這可能會失敗(非常糟糕)。在這種情況下strlen(guess)為 0,我們最終會訪問guess[-1]以查看它是否是換行符,這是未定義且錯誤的。在實踐中,回傳一個空字串可能是不可能的fgets(至少,只要你給它一個大于 1 的緩沖區來讀取),但以更安全的方式撰寫代碼可能比說服自己它可以更容易不會發生。SO上的其他地方有很多關于實際有效地檢測fgets沒有成功閱讀整行的案例的問題,但現在我找不到任何一個。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/474092.html
