第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参考手册。