日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++ scanf()函数安全性问题

發布時間:2024/10/5 c/c++ 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ scanf()函数安全性问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

scanf()是C語言中的一個輸入函數。與printf函數一樣,都被聲明在頭文件stdio.h里,因此在使用scanf函數時要加上#include <stdio.h>。(在有一些實現中,printf函數與scanf函數在使用時可以不使用預編譯命令#include <stdio.h>。)它是格式輸入函數,即按用戶指定的格式從鍵盤上把數據輸入到指定的變量之中。

int?scanf(const?char?*?restrict?format,...);

函數的第一個參數是格式字符串,它指定了輸入的格式,并按照格式說明符解析輸入對應位置的信息并存儲于可變參數列表中對應的指針所指位置。每一個指針要求非空,并且與字符串中的格式符一一順次對應。

函數 scanf() 是從標準輸入流stdin??(標準輸入設備,一般指向鍵盤)中讀內容的通用子程序,可以說明的格式讀入多個字符,并保存在對應地址的變量中。?

scanf函數返回成功讀入的數據項數,讀入數據時遇到了“文件結束”則返回EOF。

scanf()函數安全性問題

(1)在高版本的 Visual Studio 編譯器中,scanf 被認為是不安全的,被棄用,應當使用scanf_s代替 scanf。

(2) 對于字符串數組或字符串指針變量,由于數組名可以轉換為數組和指針變量名本身就是地址,因此使用scanf()函數時,不需要在它們前面加上"&"操作符。

(3) 可以在格式化字符串中的"%"各格式化規定符之間加入一個整數,表示任何讀操作中的最大位數。

(4) scanf函數中沒有類似printf的精度控制。

如: scanf("%5.2f",&a); 是非法的。不能企圖用此語句輸入小數為2位的實數。

(5) scanf中要求給出變量地址,如給出變量名則會出錯

如 scanf("%d",a);是非法的,應改為scanf("%d",&a);才是合法的。

(6) 在輸入多個數值數據時,若格式控制串中沒有非格式字符作輸入數據之間的間隔,則可用空格,TAB或回車作間隔。

C編譯在碰到空格,TAB,回車或非法數據(如對“%d”輸入“12A”時,A即為非法數據)時即認為該數據結束。

(7) 在輸入字符數據(%c)時,若格式控制串中無非格式字符,則認為所有輸入的字符均為有效字符。

例如:

scanf("%c%c%c",&a,&b,&c);

輸入為:

d?e?f

只有當輸入為:def(字符間無空格) 時,才能把'd'賦于a,'e'賦予b,'f'賦予c。 如果在格式控制中加入空格作為間隔,則把'd'賦予a, ' '(空格)賦予b,'e'賦予c。因為%c 只要求讀入一個字符,后面不需要用空格作為兩個字符的間隔,因此把' '作為下一個字符送給b。

scanf("%c?%c?%c",&a,&b,&c);

我們用一些例子來說明一些規則:則輸入時各數據之間可加空格。

#include<stdio.h>int?main(void){char?a,b;printf("input?character?a,b\n");scanf("%c%c",&a,&b);/*注意兩個%c之間沒有任何符號*/printf("%c%c\n",a,b);return?0;}

輸入:由于scanf函數"%c%c"中沒有空格,輸入M N,結果輸出只有M。而輸入改為MN時則可輸出MN兩字符,見下面的輸入運行情況: input character a,b

1

屏幕顯示:

#include?<stdio.h> int?main(void) {char?a,b; printf("input?character?a,b\n"); scanf("%c?%c",&a,&b);/*注意兩個%c之間的空格*/ printf("\n%c%c\n",a,b); return?0; }

?

本例表示scanf格式控制串"%c %c"之間有空格時, 輸入的數據之間可以有空格間隔。

(8) 如果格式控制串中有非格式字符則輸入時也要輸入該非格式字符。

例如:

scanf("%d,%d,%d",&a,&b,&c);

其中用非格式符“ , ”作間隔符,故輸入時應為:

5,6,7

又如:

scanf("a=%d,b=%d,c=%d",&a,&b,&c);

則輸入應為

a=5,b=6,c=7

如輸入的數據與輸出的類型不一致時,雖然編譯能夠通過,但結果將不正確。

#include?<stdio.h> int?main(void) { int?a; printf("input?a?number"); scanf("%d",&a); printf("%ld",a); return?0; }

如將scanf("%d",&a); 語句改為 scanf("%ld",&a);由于輸入數據類型為整型, 而輸出語句的格式串中說明為長整型,因此輸出結果和輸入數據不符。輸出并不是輸入的值。

輸入數據為長整型,輸入輸出數據才相等。

問題一

如何讓scanf()函數正確接受有空格的字符串?如: I love you!

#include?<stdio.h> int?main(void) { char?str[80]; scanf("%s",str); printf("%s",str); return?0; }

輸入:

I?love?you!

輸出:

I

上述程序并不能達到預期目的。因為scanf掃描到"I"后面的空格就認為對str的掃描結束(空格沒有被掃描),并忽略后面的" love you!"。值得注意的是,我們改動一下上面的程序來驗證一下:

#include<stdio.h>#include<windows.h>int?main(void){char?str[80],str1[80],str2[80];scanf("%s",str);/*此處輸入:I?love?you!*/printf("%s\n",str);Sleep(5000);/*這里等待5秒,告訴你程序運行到什么地方*//***不是sleep(5)*1,函數名是Sleep不是sleep。*2,Windows?API中,unsigned?Sleep(unsigned)應該是毫秒ms.*/scanf("%s",str1);/*這兩句無需你再輸入,是對stdin流再掃描*/scanf("%s",str2);/*這兩句無需你再輸入,是對stdin流再掃描*/printf("%s\n",str1);printf("%s\n",str2);return?0;}

輸入:

I?love?you!

輸出:

Iloveyou!

好了,原因知道了,所以結論是:殘留的信息 love you是存在于stdin流中,而不是在鍵盤緩沖區中。那么scanf()函數能不能完成這個任務?回答是:能!別忘了scanf()函數還有一個 %[] 格式控制符(如果對%[]不了解的請查看本文的上篇),請看下面的程序

#include<stdio.h>int?main(void){char?str[50];scanf("%[^\n]",str);/*scanf("%s",string);不能接收空格符*/printf("%s\n",str);return?0;}

?

問題二

鍵盤緩沖區殘余信息問題

#include<stdio.h>int?main(void){int?a;char?c;while(c!='N'){scanf("%d",&a);scanf("%c",&c);printf("a=%dc=%c\n",a,c);/*printf("c=%d\n",c);*/}return?0;}


scanf("%c", &c);這句不能正常接收字符,什么原因呢?我們用printf("c = %d\n", c);將C用int表示出來,啟用printf("c = %d\n", c);這一句,看看scanf()函數賦給C到底是什么,結果是c=10 ,ASCII值為10是什么?換行即\n.對了,我們每擊打一下"Enter"鍵,向鍵盤緩沖區發去一個“回車”(\r),一個“換行"(\n),在這里\r被scanf()函數處理掉了(姑且這么認為吧^_^),而\n被scanf()函數“錯誤”地賦給了c.解決辦法:可以在兩個scanf()函數之后加getchar(),但是要視具體scanf()語句加那個,這里就不分析了,讀者自己去摸索吧
。

#include<stdio.h>int?main(void){int?a;char?c;while(c!='N'){scanf("%d",&a);fflush(stdin);scanf("%c",&c);fflush(stdin);printf("a=%dc=%c\n",a,c);}return?0;}


版本1:運行出錯的程序這里再給一個用“空格符”來處理緩沖區殘余信息的示例:

#include<stdio.h>int?main(void){int?i;char?j;for(i=0;i<10;++i)scanf("%c",&j);/*這里%前沒有空格*/printf("%c",j);/*在輸入十個字符之后*/return?0;}

版本2:使用了空格控制符后

#include<stdio.h>int?main(void){int?i;char?j;for(i=0;i<10;++i)scanf("?%c",&j);/*注意這里%前有個空格*/printf("%c",j);/*在輸入十個字符之后,驗證打印出來的字符是否是自己輸入的最后一個字符(即輸入的第十個字符)*/return?0;}

我們輸入:接著,我們運行看看,首先,運行第一個版本(錯誤的程序)

0 1 2 3 4 5 6 7 8 9

結果是一個空字符

再運行第二個版本(正確的程序)

同樣輸入:

0 1 2 3 4 5 6 7 8 9

這一次就顯示字符9,故此程序正確。

那么為什么第二個程序就正確呢,原因何在,在%前面加一個空格就這么有用,答案是肯定的,就是%前面的空格在起作用,讀者看看此文章的前面部分,在scanf的使用過程中應注意的問題中已經指出:“scanf()的格式控制串可以使用空白字符或其它非空白字符,使用空白字符會使scanf()函數在讀操作中略去輸入中的零個或多個空白字符。”

所以在%前面加上了空格(空格屬于空白字符,此外還有像制表符等也屬于空白字符),在輸入過程中,將略去輸入中的一個或多個空白字符,所以我們輸入的0 1 2 3 4 5 6 7 8 9這些字符中的空白字符就被略去了,字符9也就正確的打印出來了,這樣子解釋,相信大家都看明白勒吧!

問題三

輸入類型與格式化字符串不匹配導致stdin流的阻塞。

#include<stdio.h>int?main(void){int?a=0,b=0,c=0,ret=0;ret=scanf("%d%d%d",&a,&b,&c);printf("第一次讀入數量:%d\n",ret);ret=scanf("%d%d%d",&a,&b,&c);printf("第二次讀入數量:%d\n",ret);return?0;}

正確輸入的話:我們定義了a,b,c三個變量來接受輸入的內容,定義了變量ret來接收scanf函數的返回值。

但是當輸入內容與格式換字符串不匹配時,結果會令人大跌眼鏡(仔細分析會對scanf函數和stdin流有更深入的哦):

執行到第一個scanf時,當輸入字符’b’的時候與ret=scanf("%d%d%d",&a,&b,&c);中的格式化字符串不匹配,stdin流被阻塞,scanf函數不在讀取后面的部分,直接將1返回,表示只將stdin流中的1讀入到了變量a中。

執行到第二個scanf時,字符’b’還是與格式化字符串不匹配,stdin流仍然被阻塞,所以沒有提示輸入,scanf函數將0返回。

將代碼作如下修改,可以有力的證明上述結論。

#include<stdio.h>int?main(void){int?a=0,b=0,c=0,ret=0;ret=scanf("%d%d%d",&a,&b,&c);printf("第一次讀入數量:%d\n",ret);ret=scanf("%c%d%d",&a,&b,&c);printf("第二次讀入數量:%d\n",ret);return?0;}

當把第二個scanf函數內的格式化字符串改為”%c%d%d”時,運行結果如下:

執行到第一個scanf函數時,由于輸入’b’的原因scanf函數直接返回1,stdin流阻塞。

執行到第二個scanf函數時,字符’b’與格式化字符串”%c%d%d”中的%c匹配,stdin流終于疏通,在輸入6,則將變量a,b,c分別賦值為98(‘b’的ASCII碼)、2、6,scanf函數返回3。

有上述問題可知,當使用scanf函數時,如果遇到一些匪夷所思的問題,在scanf函數后正確使用fflush(stdin);,清空輸入緩沖區,可以解決很多問題。以本題為例:

#include<stdio.h>int?main(void){int?a=0,b=0,c=0,ret=0;ret=scanf("%d%d%d",&a,&b,&c);fflush(stdin);printf("第一次讀入數量:%d\n",ret);ret=scanf("%d%d%d",&a,&b,&c);fflush(stdin);printf("第二次讀入數量:%d\n",ret);return?0;}

運行結果:

問題解決。

問題四

如何處理scanf()函數誤輸入造成程序死鎖或出錯

#include<stdio.h>int?main(void){int?a,b,c;scanf("%d,%d",&a,&b);c=a+b;/*計算a+b*/printf("%d+%d=%d",a,b,c);return?0;}

如上程序,如果正確輸入a,b的值,那么沒什么問題,但是,你不能保證使用者每一次都能正確輸入,一旦輸入了錯誤的類型,你的程序不是死鎖,就是得到一個錯誤的結果,呵呵,這可能所有人都遇到過的問題吧?解決方法:scanf()函數執行成功時的返回值是成功讀取的變量數,也就是說,你這個scanf()函數有幾個變量,如果scanf()函數全部正常讀取,它就返回幾。但這里還要注意另一個問題,如果輸入了非法數據,鍵盤緩沖區就可能還個有殘余信息問題。正確的例程

#include<stdio.h>int?main(void){int?a,b,c;while(scanf("%d%d",&a,&b)!=2)fflush(stdin);c=a+b;printf("%d+%d=%d",a,b,c);return?0;}

fflush(stdin)這個方法在GCC下不可用。(在VC6.0下可以)補充

以下是 C99 對?fflush?函數的定義:

int fflush(FILE *stream);

如果stream指向輸出流或者更新流(update stream),并且這個更新流

執行的操作不是輸入,那么fflush函數將把任何未被寫入的數據寫入stream

指向的文件(如標準輸出文件stdout)。否則,fflush函數的行為是不確定的。

C和C++的標準里從來沒有定義過?fflush(stdin)。

fflush(NULL)清空所有輸出流和上面提到的更新流。如果發生寫錯誤,fflush

函數會給那些流打上錯誤標記,并且返回EOF,否則返回0。

由此可知,如果 stream 指向輸入流(如 stdin),那么 fflush 函數的行為是不確定的。故而使用

fflush(stdin) 是不正確的,至少是移植性不好的。

可采用如下方法:

方法一:

?

/*此函數可以和scanf函數一起使用,但使用%c輸入時要注意,即此函數只能用于緩沖區非空的情況*/#include<stdio.h>void?flush(){char?c;while((c=getchar())!='\n'&&c!=EOF);}intmain(void){int?a,b,c;/*計算a+b*/while(scanf("%d%d",&a,&b)!=2)flush();c=a+b;printf("%d+%d=%d",a,b,c);return?0;}

方法二:

使用getchar()代替fflush(stdin)

程序示例:

#include<stdio.h>int?main(void){inti,c;while(1){printf("Pleaseinputaninteger:");scanf("%d",&i);if(feof(stdin)||ferror(stdin)){//如果用戶輸入文件結束標志(或文件已被讀完),或者發生讀寫錯誤,則退出循環//dosomethingbreak;}//沒有發生錯誤,清空輸入流。通過while循環把輸入流中的余留數據“吃”掉while((c=getchar())!='\n'&&c!=EOF);//可直接將這句代碼當成fflush(stdin)的替代,直接運行可清除輸入緩存流//使用scanf("%*[^\n]");也可以清空輸入流,不過會殘留\n字符。printf("%d\n",i);}return?0;}

?

總結

以上是生活随笔為你收集整理的C++ scanf()函数安全性问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。