危險函數gets()幾種完美的替代方法 你可能還不知道的

gets()用處

gets從標準輸入設備讀字符串函數,其可以無限讀取,不會判斷上限,可以包含空格,以回車結束讀取。

gets()的危險之處

因爲該函數可以無限讀取,所以應該確保buffer的空間足夠大,以便在執行讀操作時不發生溢出。如果溢出,多出來的字符將被寫入到堆棧中,這就覆蓋了堆棧原先的內容,破壞一個或多個不相關變量的值。這個事實導致gets函數只適用於玩具程序。

gets()的幾種替代方法

既然gets()的用處是用來讀取一個包含空格的字符串,那麼我們就有了以下幾種方法來代替gets():

一、用%c循環輸入直到遇到換行結束

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<string>
using namespace std;
int main()
{
    char str[100];
    char ch;
    int i=0;
    while(scanf("%c", &ch) && ch != '\n')
    {
        str[i++] = ch;
    }
    cout << str << endl;
    return 0;
}

我們可以用上面的方法來讀取一個包含空格的字符串,但是實際操作中遇到了下面的情況:

從圖片中可以看出,我們給str輸入的是“123 456  789”,但是輸出結果卻並不是我們想要的,這是爲什麼呢?

答案很簡單,當我們輸出str字符串的時候,系統是以'\0'符號來判斷一個字符串的末尾的,我們輸入遇到'\n'的時候就跳出循環了,所以後面的內容是不可預知的,直到遇到'\0'才停止輸出。

要怎樣解決呢?

更簡單了,既然字符串需要以'\0'結束,那我們只需要把字符串的末尾的那個字符手動賦值爲'\0'即可:

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int main()
{
    char str[100];
    char ch;
    int i=0;
    while(scanf("%c", &ch) && ch != '\n')
    {
        str[i++] = ch;
    }
    str[i] = '\0';    //手動吧字符串末尾字符賦值成'\0'
    cout << str << endl;
    return 0;
}

這個時候我們再來驗證一下,發現問題就解決了:

二、用getchar()循環輸入直到遇到換行結束

這個方法從原理是跟上面的方法是一樣的,只是寫法不一樣,下面直接放上參考代碼:(值得注意的是末尾仍要賦成'\0')

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int main()
{
    char str[100];
    char ch;
    int i=0;
    while((ch = getchar()) != '\n')
    {
        str[i++] = ch;
    }
    str[i] = '\0';    //手動吧字符串末尾字符賦值成'\0'
    cout << str << endl;
    return 0;
}

三、scanf的另一種用法

我們知道用scanf的%s可以用來輸入一個字符串,但是%s遇到空格之後便停止了,不能達到輸入空格的效果,所以我們可以使用另一種方法:

scanf("%[^\n]%*c", str);

看似很複雜的一句代碼,下面我們來解讀一下:

這句話的意思是碰見了回車就退出,然後把緩衝區裏面的內容按字符串格式輸入str中,回車依然留在緩衝區。

其中"%[^\n]"表示讀入一個字符串,遇到'\n'停止,並設置末尾的'\0'。^ 是“非”的意思,意思就是說把一個非“\n”字符讀入字符串,直到遇到“\n”停止輸入。

而“%*c”呢,則是代表讀入一個字符到緩衝區,但是不向任何地方輸入。這樣,就解決了字符串後邊的“\n”對下面數據的影響,如果不加“%*c”的話,則大多數情況下需要在scanf前加一句getchar()來消除回車的影響。

附:

其實所有對%s起作用的控制都可以用%[],比如%[0-9]表示只讀入'0'到'9'之間的字符,%[a-zA-Z]表示只讀入字母,'-'是範圍連接符,當然也可以直接列出你需要讀入的字符。
如果你只需要讀"abc"裏面的字符就可以用%[abc] (或者%[cab]、%[acb]、%[a-c]、%[c-a].....),
如果想讀入某個範圍之外的字符串就在前面加一個'^',如:%[^a-z]就表示讀入小寫字母之外的字符。
例如從鍵盤輸入的"1235ab86"中讀取1235、86給n,有如下方法:

#include <stdio.h>
bool skip(){
     scanf("%*[^0-9]");
     return true;
}
void main()
{
      int n;
      while(skip() && scanf("%d", &n)!=EOF)
        printf("%d\n", n);
}


輸出爲:

1235

86

四、c++中的getline()方法

getline不是C庫函數,而是gcc的擴展定義或者C++庫函數。它會生成一個包含一串從輸入流讀入的字符的字符串。

具體用法:

getline(cin, str);

需要注意的是,str字符串必須是C++中的string字符串類型

也就是說必須包含頭文件

#include<string>

並且str必須定義爲string類型

string str;

需要注意的是,既然str定義的是string類型,則說明求字符串長度函數strlen()將不再可用,C++提供了另一種方法:

int len = str.size();

下面來驗證一下:

 

以上即爲gets()函數的幾種替代方法,如有疑問或者建議,歡迎評論批評指正。

其中,方法三中部分參考了這篇博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章