第3章 庫函數問題
本章主要介紹庫函數的使用中會遇到的問題。使用庫函數可以降低軟件開發的難度,提高代碼編寫的效率。這一章主要涵蓋的內容有,調用字符串庫函數時需要注意對字符串結束符'\0'的處理,複製字符串的時候要注意內存空間是否寫溢出,函數調用前需要做必要的初始化,函數使用後對其返回值需要做正確處理,容器類的增、刪操作要注意迭代器失效等,忽視這些方面將帶來各種各樣的問題。
3.1 sprintf函數引起的緩衝區溢出
代碼示例
- int main()
- {
- char src[50] = "abcdefghijklmnopqrstuvwxyz";
- char buf[10] = "";
- int len = sprintf(buf, "%s", src);
- printf("src=%s\n", src);
- printf("len=%d\n", len);
- printf("buf=%s\n", buf);
- return 0;
- }
現象&後果
程序運行時,用sprintf函數把字符數組src的內容往字符數組buf複製時會溢出,可能出現段錯誤(Segmentation fault)。
Bug分析
上述代碼從一個字符數組src複製字符串到另外一個字符數組buf中,src的字符串長度爲26,但buf的長度只有10,用sprintf函數進行復制的時候會把src的所有字符往buf裏寫,從而引起buf溢出。
正確的做法是在複製之前檢查buf的長度是否足夠,或者直接用更安全的snprintf函數代替sprintf。
正確代碼
- int main()
- {
- char src[50] = "abcdefghijklmnopqrstuvwxyz";
- char buf[10] = "";
- int len = snprintf(buf, sizeof(buf), "%s", src);
- if(len > sizeof(buf) - 1)
- {
- printf("[Error] Source string length is %d. The buf size %d is not enough. Copy incomplete!\n", len, sizeof(buf));
- }
- else
- {
- printf("src=%s\n", src);
- printf("len=%d\n", len);
- printf("buf=%s\n", buf);
- }
- return 0;
- }
編程建議
在libc參考手冊對sprintf函數的說明中有一個警告,如果複製的字符串長度超過提供的buf串的長度,sprintf函數會變得很危險。爲了避免這個問題,可以用snprintf函數來代替sprintf函數。但在使用snprintf的時候,在調用這個函數之後需要對返回值作檢查,如果返回值比分配的buf長度要大,表示複製不完整,則需要重新分配大的空間之後再一次調用snprintf函數。
在libc參考手冊中,也同時提到,在實際使用過程中,用asprintf函數代替snprintf函數會更方便些。asprintf函數不需要預先分配buf,它能在複製過程中根據實際複製源字符串的大小動態分配空間,具體可參考libc參考手冊。