gets與fgets,puts與fputs區別

2021-09-07 19:32:47 字數 3479 閱讀 5467

一.gets與fgets

//gets函式很危險,gets沒有指定輸入字元的大小,限制輸入緩衝區的大小,如果輸入的字元大於定義的陣列長度,會發生記憶體越界,堆疊溢位。後果嚴重!

對於 gets 函式,它的任務是從 stdin 流中讀取字串,直至接收到換行符或 eof 時停止,並將讀取的結果存放在 buffer 指標所指向的字元陣列中。這裡需要注意的是,換行符不作為讀取串的內容,讀取的換行符被轉換為 null(』\0』) 值,並由此來結束字串。即換行符會被丟棄,然後在末尾新增 null(』\0』) 字元。其函式的原型如下:

char* gets(char* buffer);

如果讀入成功,則返回與引數 buffer 相同的指標;如果讀入過程中遇到 eof 或發生錯誤,返回 null 指標。因此,在遇到返回值為 null 的情況,要用 ferror 或 feof 函式檢查是發生錯誤還是遇到 eof。

函式 gets 可以無限讀取,不會判斷上限,所以程式設計師應該確保 buffer 的空間足夠大,以便在執行讀操作時不發生溢位。也就是說,gets 函式並不檢查緩衝區 buffer 的空間大小,事實上它也無法檢查緩衝區的空間。

如果函式的呼叫者提供了乙個指向堆疊的指標,並且 gets 函式讀入的字元數量超過了緩衝區的空間(即發生溢位),gets 函式會將多出來的字元繼續寫入堆疊中,這樣就覆蓋了堆疊中原來的內容,破壞乙個或多個不相關變數的值。如下面的示例**所示:

int main(void)

示例**的執行結果為:

aaa輸出: aaa

根據執行結果,當使用者在鍵盤上輸入的字元個數大於緩衝區 buffer 的最大界限時,gets 函式也不會對其進行任何檢查,因此我們可以將惡意**多出來的資料寫入堆疊。由此可見,gets 函式是極其不安全的,可能成為病毒的入口,因為 gets 函式沒有限制輸入的字串長度。所以我們應該使用 fgets 函式來替換 gets 函式,實際上這也是大多程式設計師所推薦的做法。

相對於 gets 函式,fgets 函式最大的改進就是能夠讀取指定大小的資料,從而避免 gets 函式從 stdin 接收字串而不檢查它所複製的緩衝區空間大小導致的快取溢位問題。當然,fgets 函式主要是為檔案 i/o 而設計的(注意,不能用 fgets 函式讀取二進位制檔案,因為 fgets 函式會把二進位制檔案當成文字檔案來處理,這勢必會產生亂碼等不必要的麻煩)。其中,fgets 函式的原型如下:

char *fgets(char *buf, int bufsize, file *stream);

該函式的第二個引數 bufsize 用來指示最大讀入字元數。如果這個引數值為 n,那麼 fgets 函式就會讀取最多 n-1 個字元或者讀完乙個換行符為止,在這兩者之中,最先滿足的那個條件用於結束輸入。

與 gets 函式不同的是,如果 fgets 函式讀到換行符,就會把它儲存到字串中,而不是像 gets 函式那樣丟棄它。即給定引數 n,fgets 函式只能讀取 n-1 個字元(包括換行符)。如果有一行超過 n-1 個字元,那麼 fgets 函式將返回乙個不完整的行(只讀取該行的前 n-1 個字元)。但是,緩衝區總是以 null(』\0』) 字元結尾,對 fgets 函式的下一次呼叫會繼續讀取該行。

也就是說,每次呼叫時,fgets 函式都會把緩衝區的最後乙個字元設為 null(』\0』),這意味著最後乙個字元不能用來存放需要的資料。所以如果某一行含有 size 個字元(包括換行符),要想把這行讀入緩衝區,要把引數 n 設為 size+1,即多留乙個位置儲存 null(』\0』)。

最後,它還需要第 3 個引數來說明讀取哪個檔案。如果是從鍵盤上讀入資料,可以使用 stdin 作為該引數,如下面的**所示:

int main(void)

對於上面的示例**,如果輸入的字串小於或等於 10 個字元,那麼程式將完整地輸出結果;如果輸入的字串大於 10 個字元,那麼程式將截斷輸入的字串,最後只輸出前 10 個字元。示例**執行結果為:

aaaaaaaaaaaaaaaa

輸出: aaaaaaaaaa

除此之外,c99 還提供了 fgets 函式的寬字元版本 fgetws 函式,其函式的一般原型如下面的**所示:

wchar_t *fgetws(wchar_t * restrict s, int n, file * restrict stream);

該函式的功能與 fgets 函式一樣。

二.puts與fputs的區別:

與 gets 函式一樣,對於 puts 函式,同樣建議使用 fputs 函式來代替 puts 函式。如下面的示例**所示:

int main(void)

其中,puts 函式的原型如下所示:

int puts(const char *str);

我們知道,puts 函式主要用於向標準輸出裝置(螢幕)寫入字串並換行,即自動寫乙個換行符(』\n』)到標準輸出。理論上,該函式的作用與「printf("%s\n",str);」語句相同。但是,puts 函式只能輸出字串,不能進行相關的格式變換。與此同時,它需要遇到 null(』\0』) 字元才停止輸出。因此,非字串或無 null(』\0』) 字元的字元陣列最好不要使用該函式列印,否則無法正常結束。如下面的**所示:

int main(void)

;puts(str);

return 0;

}在上面的示例**中,因為字元陣列 str 在結尾處缺少乙個 null(』\0』) 字元(也就是說它不是乙個嚴格意義上的字串)。因此,在呼叫 puts 函式的時候,程式將不知道什麼時候停止輸出,從而導致輸出結果未定義。執行結果如下圖所示:

圖 1 示例**的執行結果(microsoft visual studio 2010)

正確的做法是應該在字元陣列 str 的結尾處新增乙個 null(』\0』) 字元,如下面的示例**所示:

char str = ;

fputs 函式的函式原型如下所示:

int fputs(const char *str, file *stream);

相對於 puts 函式,fputs 函式用來向指定的檔案寫入乙個字串(不換行)。當然,也可以使用 stdout 作為引數進行輸出顯示(它同樣需要遇到 null(』\0』) 字元才停止輸出),如下面的**所示:

int main(void)

;fputs(str,stdout);

return 0;

}其執行結果如下圖所示:

、圖 2 示例**的執行結果(microsoft visual studio 2010)

當然,fputs 函式主要用於對指定檔案進行寫入操作,如下面的示例**所示:

int main(void)

fputs(「this is a test」, fp);

fclose(fp);

fp=null;

return 0;

}執行上面的示例**,檔案「myfile.txt」會被寫入一行「this is a test」字串。

與 fgetws 一樣,c99 同樣也提供了 fputs 函式的寬字元版本 fputws,其函式的一般原型如下面的**所示:

int fputws(const wchar_t * restrict s, file * restrict stream);

fgets與gets比較分析

函式名 fgets 功能 從流中讀取乙個字串 用法 char fgets char string,int n,file stream 形參注釋 函式說明 fgets 用來從引數stream所指的檔案內讀入字元並存到引數s所指的記憶體空間,直到出現換行字元 讀到檔案尾或是已讀了size 1個字元為止,...

fgets與gets的區別

首先看看他們各自的定義 gets 從stdin流中讀取字串,直至接受到換行符或eof時停止,並將讀取的結果存放在buffer指標所指向的字元陣列中。換行符不作為讀取串的內容,讀取的換行符被轉換為 0 空字元,並由此來結束字串。fgets 讀取的資料儲存在buf指向的字元陣列中,每次最多讀取bufsi...

gets 與 scanf 的區別

scanf 函式和gets 函式都可用於輸入字串,但在功能上有區別。若想從鍵盤上輸入字串 hellow word 則應該使用gets 函式。gets可以接收空格 而scanf遇到空格 回車和tab鍵都會認為輸入結束,所有它不能接收空格。char string 15 gets string 遇到回車認...