掘根寶典之C語言字符串輸入函數(gets(),fgets(),get_s())

字符串輸入前的注意事項

如果想把一個字符串讀入程序,首先必須預留該字符串的空間,然后用輸入函數獲取該字符串

?這意味著必須要為字符串分配足夠的空間

不要指望計算機在讀取字符串時順便計算它的長度,然后再分配空間(計算機不會這樣做,除非你編寫一個處理這些任務的函數)。

假設編寫了如下代碼:

char *name;
scanf("%s",name);

雖然可能會通過編譯(編譯器很可能給出警告),但是在讀入name時,name可能會擦寫掉程序中的數據或代碼,從而導致程序異常中止。

因為scanf()要把信息拷貝至參數指定的地址上,而此時該參數是個未初始化的指針,name可能會指向任何地方。大多數程序員都認為出現這種情況很搞笑,但僅限于評價別人的程序時。
?

最簡單的方法是,在聲明時顯式指明數組的大小:
?

char name[81];
scanf("%s",name);


現在name是一個已分配塊(81字節)的地址。還有一種方法是使用C庫函數來分配內存

為字符串分配內存后,便可讀入字符串。C語言提供了許多讀取字符串的函數:gets(),fgets(),gets_s()函數

gets()

C語言中的gets()函數是一個標準庫函數,用于從標準輸入(鍵盤)讀取一行字符并存儲到指定的字符數組中。它的函數原型如下:

char *gets(char *str);

gets()函數的參數是一個字符數組,用于存儲讀取到的字符,返回值是讀取到的字符數組的首地址。

注意:gets()函數存在一些安全性問題,因為它無法保證讀取的字符數不超過指定的字符數組大小,可能導致緩沖區溢出。因此,在實際的程序開發中,最好使用更安全的替代函數fgets()來代替gets()函數。

特點

在讀取字符串時,scanf()和轉換說明%s只能讀取一個單詞。可是在程序中經常要讀取一整行輸入,而不僅僅是一個單詞。許多年前,gets()函數就用于處理這種情況。

gets()函數簡單易用,它讀取整行的輸入,直至遇到換行符,然后丟棄換行符,儲存其余字符,并在這些字符的末尾添加一個空字符使其成為一個C字符串。

使用示例:

#include <stdio.h>
int main()
{
char words[81];gets(words) ; // 典型用法printf("s\n", words);
}

結果

//輸入abcd
abcd

但是我們拿著上面這段代碼去運行的時候,就會發現編譯器報錯或者發出警告,這是為什么呢?

缺點

問題就出現在gets唯一的參數是words,它無法檢查數組是否裝得下輸入行。

因此gets()函數只知道數組的開始處(通過傳入的數組名),但是并不知道數組中有多少個元素。

如果輸入的字符串過長,會導致緩沖區溢出(buffer overflow),即多余的字符超出了指定的目標空間。

如果這些多余的字符只是占用了尚未使用的內存,就不會立即出現問題;

如果它們擦寫掉程序中的其他數據,會導致程序異常中止:或者還有其他情況。

為了讓輸入的字符串容易溢出,把程序中的STLEN設置為5,程序的輸出如下:

//輸入abcd
abcd
Segmentation fault:11


“Segmentation fault”(分段錯誤)似乎不是個好提示,的確如此。在UNIX系統中,這條消息說明該程序試圖訪問未分配的內存。

C 提供解決某些編程問題的方法可能會導致陷入另一個尷尬棘手的困境。

但是,為什么要特別提到gets()函數?

因為該函數的不安全行為造成了安全隱患。

過去,有些人通過系統編程,利用gets()插入和運行一些破壞系統安全的代碼。
不久,C編程社區的許多人都建議在編程時摒棄gets()。制定C99標準的委員會把這些建議放入了標準,承認了gets()的問題并建議不要再使用它。盡管如此,在標準中保留gets()也合情合理,因為現有程序中含有大量使用該函數的代碼。而且,只要使用得當,它的確是一個很方便的函數。
好景不長,C11標準委員會采取了更強硬的態度,直接從標準中廢除了gets()函數。然而在實際應用中,編譯器為了能兼容以前的代碼,大部分都繼續支持gets()函數。不過,VS2022就不支持了

fgets()

過去通常用fgets()來代替gets(),fgets()函數稍微復雜些,在處理輸入方面與gets()略有不同。

原型

在C語言中,fgets()函數用于從指定的輸入流中讀取一行字符串。它接受三個參數:輸入緩沖區指針,緩沖區大小和要讀取的輸入流。

使用fgets()函數的語法如下:

char *fgets(char *str, int size, FILE *stream);

fgets()函數的第2個參數指明了讀入字符的最大數量。

如果該參數的值是n,那么fgets()函數從輸入流中讀取至多n?- 1個字符(因為會自動加\0),或者遇到換行符('\n')為止。

fgets()函數的第3個參數指明要讀入的文件。如果讀入從鍵盤輸入的數據,則以stdin(標準輸入)作為參數,該標識符定義在stdio.h中。

fgets()函數返回指向char的指針。如果一切進行順利,該函數返回的地址與傳入的第1個參數相同但是,如果函數讀到文件結尾,它將返回一個特殊的指針:空指針(null pointer)。該指針保證不會指向有效的數據,所以可用于標識這種特殊情況。在代碼中,可以用數字0來代替,不過在C語言中用宏NULL來代替更常見(如果在讀入數據時出現某些錯誤,該函數也返回NULL)。

讀取規則

它將讀取到的字符逐個存儲在字符數組中,直到達到指定的大小或者遇到換行符為止。

如果沒有遇到換行符,或者輸入流中沒有更多字符可讀,fgets()函數會在最后一個字符后面添加一個空字符('\0')(如果數組沒存滿,系統會自動添加\0直到裝滿),表示字符串的結束。

看個例子

#include<stdio.h>
int main()
{char a[10];fgets(a, 10, stdin);printf("%s", a);}

輸入1234567890(超出指定大小),結果是

123456789

?輸入1234,按enter(遇到換行符),結果是

1234

我們可以再看個例子啊

#include <stdio.h>
int main(void)
{char words[10];puts("Enter strings (empty line to quit):");while (fgets(words, 14, stdin) != NULL && (words[0] != '\n'))fputs(words, stdout);puts("Done.");
}

輸入By the way,the gets() function,?結果是

有人就會有疑問了啊,這輸入的東西不是超除了words的大小嗎?那為什么還能正常打印?

實際上它確實超過了,但是這是循環!

程序中的fgets()一次讀入 10?-1個字符(該例中為9個字符)。所以,一開始它只讀入了“By the wa”,并儲存為By the wa\0:接著fputs()打印該字符串,而且并未換行然后while循環進入下一輪迭代,fgets()繼續從剩余的輸入中讀入數據,即讀入“y,the ge”并儲存為y,the ge\0接著fputs()在剛才打印字符串的這一行接著打印第2次讀入的字符串。然后while 進入下一輪迭代,fgets()繼續讀取輸入、fputs()打印字符串,這一過程循環進行,直到讀入最后的“tion\n”fgets()將其儲存為tion\n\0,fputs()打印該字符串,由于字符串中的\n,光標被移至下一行開始處。

保留換行符

需要注意的是,fgets()函數會保留輸入流中的換行符,所以讀取到的字符串可能包含換行符。如果你希望去除換行符,可以使用strtok()或者手動處理字符串

系統采用緩沖的IO,這意味著用戶在按下enter鍵之前,輸入都會被存在臨時存儲區(緩沖區)。

這點與gets()不同,gets()會丟棄換行符。

我們可以先借用puts()函數的特性:自動在字符串末尾加換行符

我們可以驗證一下

#include <stdio.h>
int main(void)
{char words[14];puts("請輸入:");fgets(words, 14, stdin);printf("見證奇跡的時刻:\n");puts(words);printf("sjajj");}

結果是:?

apple pie,比fgets()讀入的整行輸入短,因此,apple pie\n\0被儲存在數組中(因為fgets()會自動存儲換行符)。當puts()顯示該字符串時又在末尾添加了換行符,調用puts()apple pie\n\0里的換行符起作用將光標移動到下一行,但是puts自動在字符串末尾添加換行符,所以光標再次移動到下一行。因此apple pie下面有一行空行。

系統使用緩沖的I/O。這意味著用戶在按下Return鍵之前,輸入都被儲存在臨時存儲區(即,緩沖區)中。按下Enter鍵就在輸入中增加了一個換行符,并把整行輸入發送給fgets()。對于輸出,fputs()把字符發送給另一個緩沖區,當發送換行符時,緩沖區中的內容被發送至屏幕上。

fgets()儲存換行符有好處也有壞處。

壞處是你可能并不想把換行符儲存在字符串中,這樣的換行符會帶來一些麻煩。

好處是對于儲存的字符串而言,檢查末尾是否有換行符可以判斷是否讀取了一整行。如果不是一整行,要妥善處理一行中剩下的字符。

處理掉換行符


首先,如何處理掉換行符?

一個方法是在已儲存的字符串中查找換行符,并將其替換成空字符:

while (words[i] !='\n')// 假設\n在words中
i++;
words[i]='\0';


其次,如果仍有字符串留在輸入行怎么辦?

一個可行的辦法是,如果目標數組裝不下一整行輸入,就丟棄那些多出的字符

while(getchar()!='\n')
contine;

gets_s()函數

C11標準新增的gets_s()函數也可代替gets()。該函數與gets()函數更接近,而且可以替換現有代碼中的 gets()。但是,它是stdio.h.輸入/輸出函數系列中的可選擴展,所以支持C11的編譯器也不一定支持它。

在C語言中,gets_s()函數用于讀取用戶輸入的字符串。gets_s()函數的聲明如下:

char *gets_s(char *str, rsize_t n);

其中,str是指向字符數組的指針,用于存儲讀取到的字符串;n表示字符數組的大小。

gets_s()函數會讀取用戶輸入的字符串,并將其存儲到指定的字符數組中,直到讀取到換行符或數組大小的限制。讀取到的字符串將包含換行符,且以\0字符結尾。

需要注意的是,gets_s()函數是C11中引入的安全版本的函數,主要解決了gets()函數的緩沖區溢出問題。

C11新增的gets_s()函數(可選)和fgets()類似,用一個參數限制讀入的字符數。


特性

  1. gets_s()只從標準輸入中讀取數據,所以不需要第3個參數。
  2. 如果gets_s()讀到換行符,會丟棄它而不是儲存它。
  3. 如果gets_s()讀到最大字符數都沒有讀到換行符,會執行以下幾步。首先把目標數組中的首字符設置為空字符,讀取并丟棄隨后的輸入直至讀到換行符或文件結尾,然后返回空指針。接著,調用依賴實現的“處理函數”(或你選擇的其他函數),可能會中止或退出程序。

第2個特性說明,只要輸入行未超過最大字符數,gets_s()和gets()幾乎一樣,完全可以用gets_s()換gets()。第3個特性說明,要使用這個函數還需要進一步學習。

三種輸入方式的選擇

我們來比較一下gets()、fgets()和gets_s()的適用性。

如果目標存儲區裝得下輸入行,3個函數都沒問題。但是fgets()會保留輸入末尾的換行符作為字符串的一部分,要編寫額外的代碼將其替換成字符。

如果輸入行太長會怎樣?

使用gets()不安全,它會擦寫現有數據,存在安全隱患。gets_s()函數很全,但是,如果并不希望程序中止或退出,就要知道如何編寫特殊的“處理函數”。另外,如果打算讓程繼續運行,gets_s(會丟棄該輸入行的其余字符,無論你是否需要。由此可見,當輸入太長,超過數組容納的字符數時,fgets()函數最容易使用,而且可以選擇不同的處理方式。如果要讓程序繼續使用輸中超出的字符,可以參考程序清單11.8中的處理方法。
所以,當輸入與預期不符時,gets_s()完全沒有fgets()函數方便、靈活。也許這也是gets s二民的因之一。鑒于此,fgets()通常是處理類似情況的最佳選擇。
?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/712834.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/712834.shtml
英文地址,請注明出處:http://en.pswp.cn/news/712834.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ai圖生文的軟件!分享4個受歡迎的!

在數字化時代&#xff0c;隨著人工智能技術的飛速發展&#xff0c;AI圖生文軟件已經成為自媒體人、創作者和廣告從業者手中的得力助手。這些軟件能夠將靜態的圖片轉化為生動的文字&#xff0c;為圖片注入靈魂&#xff0c;讓觀者仿佛置身于畫面之中。今天&#xff0c;就讓我們一…

LabVIEW和Python開發微細車削控制系統

LabVIEW和Python開發微細車削控制系統 為滿足現代精密加工的需求&#xff0c;開發了一套基于LabVIEW和Python的微細車削控制系統。該系統通過模塊化設計&#xff0c;實現了高精度的加工控制和G代碼的自動生成&#xff0c;有效提高了微細車削加工的自動化水平和編程效率。 項目…

cjson報錯

今天遇到個問題&#xff0c;使用CJSON把一個cjson對象給一個cjson對象的時候報錯&#xff0c;是segment問題 &#xff0c;原因是我在個cjson對象數據的時候&#xff0c;有幾個是char的&#xff0c;但是是個時間的字符串&#xff0c;一般20位就夠了&#xff0c;但是由于是通過mo…

1950-2022年各省逐年平均降水量數據

1950-2022年各省逐年平均降水量數據 1、時間&#xff1a;1950-2022年 2、指標&#xff1a;省逐年平均降水量 3、范圍&#xff1a;33省&#xff08;不含澳門&#xff09; 4、指標解釋&#xff1a;逐年平均降水數據是指當年的日降水量的年平均值&#xff0c;不是累計值&#…

ONLYOFFICE 桌面編輯器 v8.0 更新內容詳細攻略

文章目錄 引言PDF 表單RTL 支持電子表格中的新增功能Moodle 集成用密碼保護 PDF 文件從“開始”菜單快速創建文檔本地界面主題下載安裝桌面編輯工具總結 引言 官網鏈接&#xff1a; ONLYOFFICE 官方網址 ONLYOFFICE 桌面編輯器是一款免費的文檔處理軟件&#xff0c;適用于 Li…

面試經典 150 題 ---- 買賣股票的最佳時機 II

面試經典 150 題 ---- 買賣股票的最佳時機 II 買賣股票的最佳時機II方法一&#xff1a;貪心 買賣股票的最佳時機II 方法一&#xff1a;貪心 貪心策略&#xff0c;我們可以考慮局部最優以達到整體最優&#xff0c;僅需要判斷相鄰兩天之間的利潤是否大于 0&#xff0c;若大于 0…

uniapp實現-審批流程效果

一、實現思路 需要要定義一個變量, 記錄當前激活的步驟。通過數組的長度來循環數據&#xff0c;如果有就采用3元一次進行選擇。 把循環里面的變量【name、status、time】, 全部替換為取出的那一項的值。然后繼續下一次循環。 虛擬的數據都是請求來的, 組裝為好渲染的格式。 二…

【打工日常】使用docker部署在線PDF工具

一、Stirling-PDF介紹 Stirling-PDF是一款功能強大的本地托管的基于 Web 的 PDF 操作工具&#xff0c;使用 docker部署。該自托管 Web 應用程序最初是由ChatGPT全權制作的&#xff0c;現已發展到包含廣泛的功能來處理您的所有 PDF 需求。允許對 PDF 文件執行各種操作&#xff0…

基于session注冊JAva篇springboot

springboot3全家桶&#xff0c;數據庫 &#xff1a;redis&#xff0c;mysql 背景環境&#xff1a;郵箱驗證碼&#xff0c;驗證注冊 流程&#xff1a;先通過郵箱驗證&#xff0c;發送驗證碼&#xff0c;將獲取到的session和驗證碼&#xff0c;存入redis里&#xff08;發送郵箱…

【leetcode】鏈表的回文結構

大家好&#xff0c;我是蘇貝&#xff0c;本篇博客帶大家刷題&#xff0c;如果你覺得我寫的還不錯的話&#xff0c;可以給我一個贊&#x1f44d;嗎&#xff0c;感謝?? 點擊查看題目 思路: 1.找中間節點 找中間節點的方法在下面這個博文中詳細提過 【點擊進入&#xff1a;【l…

鴻蒙Harmony應用開發—ArkTS聲明式開發(通用屬性:布局約束)

通過組件的寬高比和顯示優先級約束組件顯示效果。 說明&#xff1a; 從API Version 7開始支持。后續版本如有新增內容&#xff0c;則采用上角標單獨標記該內容的起始版本。 aspectRatio aspectRatio(value: number) 指定當前組件的寬高比。 卡片能力&#xff1a; 從API vers…

springboot /tmp 臨時目錄

文章目錄 1.生成機制2.產生異常3.解決辦法3.1 重啟大法3.1 從Linux層面修改 /tmp目錄的清理策略3.2 增加JVM配置3.3 增加JVM配置3.4 添加spring boot配置3.5 使用配置類配置 1.生成機制 在linux系統中&#xff0c;springboot應用服務再啟動&#xff08;java -jar 命令啟動服務…

淺談 Linux 孤兒進程和僵尸進程

文章目錄 前言孤兒進程僵尸進程 前言 本文介紹 Linux 中的 孤兒進程 和 僵尸進程。 孤兒進程 在 Linux 中&#xff0c;就是父進程已經結束了&#xff0c;但是子進程還在運行&#xff0c;這個子進程就被稱作 孤兒進程。 需要注意兩點&#xff1a; 孤兒進程最終會進入孤兒院…

一篇Sora模型小白掃盲文——《Sora技術報告》總結

Sora技術報告的核心總結 根據目前公開的一些信息,我個人將Sora技術報告一文中的核心內容總結如下,分別為訓練過程的開創性優化技術、模型支持的能力、模型的缺陷。 一、訓練過程開創性優化技術 1.ChatGPT的分詞(token)處理取得了很好的一致性文本處理能力,Sora也借鑒了這…

勒索病毒普通用戶防范建議

勒索病毒普通用戶防范建議 定期備份存儲在計算機上的數據&#xff0c;這樣勒索軟件感染不會永遠破壞您的個人數據。 最好創建兩個備份副本&#xff1a;一個存儲在云中&#xff08;記住使用一個自動備份文件的服務&#xff09;&#xff0c;另一個物理存儲&#xff08;便攜式硬…

軟考-計算題

1.二維矩陣轉換成一維矩陣 2.算術表達式&#xff1a; 3.計算完成項目的最少時間&#xff1a;之前和的max&#xff08;必須之前的所有環節都完成&#xff09; 松弛時間&#xff1a;最晚開始時間-最早開始時間 最早&#xff1a;之前環節都完成的和的max 最晚&#xff1a;總時間…

基于區塊鏈技術的物聯網設備影子服務

一&#xff0e; 背景 物聯網設備影子是指真實的物聯網設備在物聯網平臺中對應的虛擬設備&#xff0c;設備影子服務存儲了對應物理設備的當前數據。用戶使用物聯網設備的實時數據都是從物聯網設備影子服務獲取&#xff0c;如果設備影子服務的數據被修改&#xff0c;就容易導致…

黑貓的牌面

解法&#xff1a; 桶 #include <iostream> #include <vector> #include <algorithm> using namespace std; #define endl \nint main() {ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);vector<int> tong(1001);int t 4;int k, pai;long lon…

LeetCode 每日一題 樹合集 Day 16 - 27

終于是開學了&#xff0c;想了想每日一更頻率太高&#xff0c;以后每周更新一周的每日一題。 103. 二叉樹的鋸齒形層序遍歷 給你二叉樹的根節點 root &#xff0c;返回其節點值的 鋸齒形層序遍歷 。&#xff08;即先從左往右&#xff0c;再從右往左進行下一層遍歷&#xff0c…

探索網絡通信與序列化:打造現代Web應用的基石20240227

在構建現代Web應用的過程中&#xff0c;理解網絡通信協議與數據序列化技術的基本概念至關重要。這些技術不僅是網絡世界的基礎設施&#xff0c;而且也是開發者設計和實現高效、可靠應用的關鍵工具。本文旨在深入淺出地介紹這些概念&#xff0c;幫助開發者更好地掌握構建Web應用…