張春玲
摘 要:scanf函數是C語言中最基本的輸入函數,本文通過分析scanf函數調用過程中的實現機制,以幫助正確使用scanf函數。
關鍵詞:C;scanf函數調用;實現機制
1 scanf函數
scanf的功能是從標準輸入設備讀取輸入的任何固有類型的數據自動轉換成機內格式并把數據輸入到指定的變量之中,返回正確讀入數值的個數。函數原型主要代碼如下:
int_cdecl scanf(const char *format,...)
{va_list arg;
va_start(arg,format);
return vscanf(_input_l,format,NULL,arg);}
2 scanf函數調用
2.1 調用格式
scanf(格式控制,地址表列);“格式控制”為格式字符串,將用戶輸入的數據轉換為指定格式;“地址表列”由若干個地址組成的參數表列,可以是變量的地址或字符串首地址。原型中_cdecl是c中的默認函數調用方式,調用函數參數自右向左入棧,因此scanf函數左邊的第一個參數format被放于棧頂。
2.2 參數入棧
C調用協議下,為遵循對齊原則,要求每個變量地址都是sizeof(int)的倍數,因此參數入棧都是整數字節。同時調用不帶原型聲明的函數時,調用者會對每個參數執行“默認實際參數提升”。從scanf函數調用格式中,可變參數是若干個地址列表(指針),C中為每個指針變量統一分配4個字節,即可變參數入棧時都占用4個字節,滿足了對齊原則,與指針指向的變量類型沒有關系。這與printf函數調用時參數入棧不一樣。
2.3 格式字符串與標準輸入流的匹配
參數入棧后,編譯器先獲取格式字符串,對照字符串中的各項,從內存緩沖區中取數據,若沒有數據,則等待用戶輸入。用戶通過鍵盤輸入數據,數據回顯于顯示器上,同時數據被存入內存緩沖區(不是鍵盤緩沖區)中。為什么?scanf源碼中,函數功能的實現依靠vscanf函數調用_input_l,而_input_l的函數功能是把鍵盤輸入數據寫入stdin(標準輸入流)來創建一個臨時交換文件的緩沖區,只有當用戶輸入回車后,scanf函數開始從內存緩沖區取數據。在接收數據時,對照字符串的各項,并按匹配規則,逐一取數:內存緩沖區中,讀取時順序讀取寫入的數據,即先讀先寫入的數據再讀后寫入的數據,這與對字符串中從左到右匹配順序一致,匹配規則如下:C99中,格式字符串有如下三種類型字符:
⑴格式說明符:遇到格式說明符去讀取緩沖區時,匹配分兩步,首先是格式字符與緩沖區數據類型匹配,然后格式字符與對應參數指向的變量類型匹配。匹配時先將緩沖區中一個或多個連續的空白字符(格式字符為字符型除外)移出并去掉,再將格式符與緩沖數據進行匹配(見(四)),若類型匹配,則讀取緩沖區中直到遇到非法字符(與指定類型不匹配的字符)或者達到輸出寬度要求前的數據,再將數據送到與格式字符類型匹配的變量中去(借助三個宏va_start;va_arg;va_end訪問后面每個參數)。若格式字符類型與緩沖區數據不匹配,stdin流被阻塞,scanf函數不在讀取后面的部分。若格式字符與后面的參數類型不一致,則丟棄數據。
因此匹配格式字符時,輸入流中開始讀取到的空白字符(尤其對字符串中多個數值型格式字符緊挨著時,需要輸入的一個或多個空白字符)對匹配沒有任何影響,自動略去。也就是匹配格式字符時,輸入流中可以輸入任意多個空白字符。如scanf(“%d”,&a);輸入時可以直接輸入整數,也可以輸入一個或者多個空白字符再輸入整數,結果一樣。
⑵空白符:可以是空格、制表符和新行符。字符串中的空白字符的作用是使scanf函數在讀操作中略去輸入流中的一個或多個空白字符。因此,空白符是使scanf在輸入流中讀,但不保存結果,直到發現非空白字符為止,同時將棧中字符串位置指向下一個非空白字符。
因此匹配空白字符時,入棧的空白字符與輸入流中不一定匹配,因為這時候字符串中的空白字符對字符串來說就是一個分隔符,輸入數據時用戶即可以輸入相同個或更多個空白字符也可以不輸入(當多個數值型格式字符由空白字符隔開時,在輸入時必須輸入空白字符),對讀取數據沒有任何影響,因為字符串中空白字符可以略去輸入流中的多個連續空白字符。
⑶非空白符:使scanf()根據棧中字符在流中讀匹配的字符并放棄。如“a=%d”,使scanf讀取流中a=并放棄,如未發現匹配,scanf()返回。若字符串中非空白字符前面沒有空白字符,在輸入數據時,一定不要輸入空白字符。例:scanf(“a=%d”,&a);輸入時第一字符只能是a,若是空格則結果不對。棧中的a與輸入流中的空格不匹配,因為空白字符不會自動略去。
2.4 格式字符匹配輸入流中的數據
如f,則告訴scanf接收數據的變量是float類型,需從輸入流中讀取一個單精度數,再放入float類型變量中;如果將 L/l放在前面,則告訴scanf接收數據的變量為double。可以看出輸入時區別%f,%lf的,這與printf函數不同。使用printf時,f遵循提升規則表示double類型。而scanf匹配時首先是根據格式字符類型讀取數據,再將數據輸入對應變量。假如scanf中f代表double,表示從輸入流讀取雙精度數,若%f對應的變量是單精度,那不能保證將一個double型的數放到一個float類型中,原因是定義變量時float,double分別分配4個字節,8個字節,向變量輸入數據時是受字節限制的。因此輸入數據時要區分float,double,%f代表兩種類型不行。
3 總結
scanf函數在C中應用比較頻繁,本文詳細分析了scanf函數調用過程,重點分析了格式字符串中三種類型的字符與內存緩沖區匹配的過程,希望對使用scanf函數有正確指導意義。
[參考文獻]
[1]譚浩強.C語言程序設計[M].北京.清華大學出版社.2011.
[2]徐娟.可變參函數scanf的執行過程分析[J].信息與電腦.2013.5.