前身——scanf()
有的教材里用的scanf(),其實在目前Visual Studio版本中已經棄用了,用scanf_s()函數代替了。
為什么現在要用scanf_s()
scanf_s()函數是Microsoft公司VS開發工具提供的一個功能相同的安全標准輸入函數,從vc++2005開始,VS系統提供了scanf_s()。在調用該函數時,必須提供一個數字以表明最多讀取多少位字符。另外,很多帶“_s”后綴的函數是為了讓原版函數更安全,傳入一個和參數有關的大小值,避免引用到不存在的元素,防止hacker利用原版的不安全性(漏洞)黑掉系統。簡單的理解,就是scanf_s會比scanf更安全,那么為了安全也需要編程者多傳一些參數,這些參數就是變量的長度(占用的字節數)。
很多帶“_s”后綴的函數是為了讓原版函數更安全,傳入一個和參數有關的大小值,避免引用到不存在的元素,有時黑客可以利用原版的不安全性黑掉系統。比如:char d[20];寫成scanf_s("%s",d,20);才是正確的,有這個參數20使准確性提高。
注意事項
下列代碼
double a,b,c;
scanf_s("%f%f%f",&a,&b,&c);
printf("%f%f%f",a,b,c);
打印結果:
512181,17989019,86000000
可以看到為3個隨機數
為啥會這樣呢?
scanf_s讀%f時,系統會按照4字節長度來讀,讀完要放到一個4字節的空間位置,也就是一個float所在的位置。那么double有8字節空間,比4字節還大,能不能來放?答案是不能。因為float和double的關系不像int和long的關系那樣,簡單的在后面增加4字節的位置。float和double有自己專門的數據排列格式,如下:如果讀的時候明明是按照float的格式來讀,但是卻存在double的空間內,並且之后一直按double來操作,那么里面數據的符號位、階碼、尾數就全錯位了。除非你在用的時候把每個double強制轉換成float來用,但是何必多次一舉?同理,如果讀用%lf來讀,卻存在float中,不僅格式錯位,而且存儲空間也不夠,會有數據丟失。所以用scanf_s讀的時候,讀%f就規規矩矩的放進float中來存,讀%lf就放進double中,這樣在使用的時候里面的數據才不會錯位。在printf的時候,首先C++里面的float其實在使用過程中都是被隱式轉換成了double來用。所以你在printf時用float還是double其實是一樣的。用%f和%lf都可以。
所以正確的寫法是
double a,b,c;
scanf_s("%lf%lf%lf",&a,&b,&c);
總結
(1)printf的%f說明符既可以輸出float型又可以輸出double型。
根據“默認參數提升”規則(在printf這樣的函數的可變參數列表中,不論作用域內有沒有原型,都適用這一規則)float型會被提升為double型。因此printf()只會看到雙精度數。
(2)scanf對於float類型必須用%f,double必須用%lf
對於scanf,情況就完全不同了,它接受指針,這里沒有類似的類型提升。(通過指針)向float存儲和向double存儲大不一樣,因此,scanf區別%f和%lf。
(3)事實上,printf中沒有定義%lf,但是很多系統可能會接受它。要確保可移植性,就要堅持使用%f。
要想保留小數位數,請使用(比如保留8位小數):
printf("%.8f",a);