用sscanf解析輸入字符串
我們平常編寫的很多應用程序都會處理各種各樣的輸入,這些輸入或來自本地文件,或來自網絡,或來自用戶的輸入。今天,讓我們來看看sscanf這個和字符串相關的函數可能給你帶來的麻煩。
下面是演示代碼,這段代碼將一些以字符串形式保存的數據加載到程序中。這些字符串可以來自任何地方,本演示代碼對字符串進行了硬編碼。
1 struct Data 2 { 3 char item1; 4 char item2; 5 char item3; 6 char item4; 7 }; 8 9 int main() 10 { 11 Data data; 12 memset(&data, 0, sizeof(data)); 13 14 char data1[] = "1"; 15 char data2[] = "2"; 16 char data3[] = "3"; 17 char data4[] = "4"; 18 19 sscanf_s(data4, "%d", &data.item4); 20 sscanf_s(data3, "%d", &data.item3); 21 sscanf_s(data2, "%d", &data.item2); 22 sscanf_s(data1, "%d", &data.item1); 23 24 printf_s("item1:%d\n", data.item1); 25 printf_s("item2:%d\n", data.item2); 26 printf_s("item3:%d\n", data.item3); 27 printf_s("item4:%d\n", data.item4); 28 29 getchar(); 30 return 0; 31 }
你覺得執行結果會是什么?
奇怪嗎?
分析
讓我們分析一下:
第19行代碼將4賦值給了item4,第20行代碼將3賦值給了item3, 第21行代碼將2賦值給了item2, 第22行代碼將1賦值給了item1。似乎沒什么問題。
還是來調試下程序吧。
下面幾張圖片演示了程序在執行過程中的內存布局
執行第一條sscanf_s前的內存布局
執行第一條sscanf_s后的內存布局
執行第二條sscanf_s后的內存布局
執行第三條sscanf_s后的內存布局
執行第四條sscanf_s后的內存布局
到這里,相信大家都已經明白程序的輸出結果為何是1,0,0,0了。
演示代碼不僅發生了內存越界,而且后續所有的輸入都覆蓋了上一條的執行結果。
罪魁禍首就是sscanf_s中的格式字符串"%d","%d"表明將輸入字符串作為一個int類型來保存到目標地址處。而演示代碼的書寫方式很顯然不符合Data這個結構體中各成員所聲明類型的要求。
將個數字符串修改為"%c"后,程序執行正確:
當然,我們其實可以在編譯階段發現這類問題
結語
通過今天這個演示,更加讓自己明白了一定要多留意編譯期間的警告信息,盡全力將其消滅。
本博客僅僅演示了sscanf_s這類函數的冰山一角,對於其他需要注意的地方可以參考Format Specifiers Checking