容易造成内存溢出的函数
c++
1、strcpy
是一种C语言的标准库函数,strcpy把含有’\0’结束符的字符串复制到另一个地址空间,返回值的类型为char*。
头文件:#include <string.h> 和 #include <stdio.h>
原型声明:char *strcpy(char* dest, const char *src);
功能:把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。返回指向dest的指针。
源码:
1 | |
举例:
1 | |
解决方法:
strncpy
strncpy函数用于将指定长度的字符串复制到字符数组中。
头文件:#include <string.h> 和 #include <stdio.h>
原型声明:char *strncpy(char *destinin, char *source, int maxlen);
功能:把source指向的字符串的前size_t n个字符(不包括\0,\0得自己手动加在*destin被复制之后)复制到destin指向的字符串中。如果要复制的*source的部分有\0,就把\0复制进去,之后就提前结束,即使没复制到第size_t n个字符也是。返回指向*destin的指针。
举例:
1 | |
参考:https://baike.baidu.com/item/strcpy/5494519?fr=aladdin
2、strcat
是一种C语言的标准库函数,将两个char类型连接,返回值的类型为char*。
头文件:#include <string.h> 和 #include <stdio.h>
原型声明:extern char *strcat(char *dest, const char *src);
功能:把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。返回指向dest的指针。
代码实现:
1 | |
举例:
1 | |
解决方法:
strncat
strncat()主要功能是在字符串的结尾追加n个字符。
头文件:#include <string.h> 和 #include <stdio.h>
原型声明:char *strncat(char *dest,char *src,int n);
功能:把src所指字符串的前n个字符添加到dest所指字符串的结尾处,并覆盖dest所指字符串结尾的’\0’,从而实现字符串的连接。strncat()会将dest字符串最后的’\0’覆盖掉,字符追加完成后,再追加’\0’。
举例:
1 | |
参考:https://baike.baidu.com/item/strncat
https://baike.baidu.com/item/strcat
3、gets
gets从标准输入设备读字符串函数,其可以无限读取,不会判断上限,以回车结束读取,所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出。
头文件:#include <stdio.h>
原型声明:char * gets (char * str);
功能:从stdin流中读取字符串,直至接受到换行符或EOF时停止,并将读取的结果存放在buffer指针所指向的字符数组中。换行符不作为读取串的内容,读取的换行符被转换为‘\0’空字符,并由此来结束字符串。
说明:由于可以无限读取,所以在2011年12月,ANSI 采纳了 ISO/IEC 9899:2011 标准,标准中删除了 gets()函数,使用一个新的更安全的函数gets_s()替代(具体用法看下面示例)。
举例:
1 | |
gets(s)函数与scanf(“%s”,s)相似,但不完全相同,使用scanf(“%s”,s) 函数输入字符串时存在一个问题,就是如果输入了空格会认为字符串结束,空格后的字符将作为下一个输入项处理,但gets()函数将接收输入的整个字符串直到遇到换行为止。
gets()函数读取到\n(我们输入的回车)于是停止读取,但是它不会把\n包含到字符串里面去。然而,和它配合使用的puts函数,却在输出字符串的时候自动换行。
解决方法:
fgets
fgets函数功能为从指定的流中读取数据,每次读取一行。
头文件:#include <string.h> 和 #include <stdio.h>
原型声明:char *fgets(char *str, int n, FILE *stream);
功能:从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
举例:
1 | |
参考:https://baike.baidu.com/item/gets
https://baike.baidu.com/item/fgets
4、sprintf
把格式化的数据写入某个字符串中,即发送格式化输出到 string 所指向的字符串。
头文件:#include <stdio.h>
原型声明:int sprintf(char *string, char *format [,argument,...]);
功能:主要功能是把格式化的数据写入某个字符串中,即发送格式化输出到 string 所指向的字符串。sprintf 是个变参函数。使用sprintf 对于写入buffer的字符数是没有限制的,这就存在了buffer溢出的可能性。
返回值
如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符。如果失败,则返回一个负数。
sprintf 返回以format为格式argument为内容组成的结果被写入string的字节数,结束字符‘\0’不计入内。即,如果“Hello”被写入空间足够大的string后,函数sprintf 返回5。
举例:
1 | |
format标签属性
format 标签属性是%[flags][width][.precision][length]specifier,具体讲解如下:
sprintf格式的规格如下所示。[]中的部分是可选的。
%[指定参数][标识符][宽度][.精度]指示符
\1. [指定参数] 处理字符方向。负号时表示从后向前处理。
\2. [标识符] 填空字元。 0 的话表示空格填 0;空格是内定值,表示空格就放着。
\3. [宽度]字符总宽度。为最小宽度。
\4. [精度] 精确度。指在小数点后的浮点数位数。
5, 转换字符
%% 印出百分比符号,不转换。
%c 字符输出到缓冲区,不转换。
%d 整数转成十进位。
%f 倍精确度数字转成浮点数。
%o 整数转成八进位。
%s 字符串输出到缓冲区,不转换。
%x 整数转成小写十六进位。
%X 整数转成大写十六进位。
例如:
1 | |
解决方法:
sprintf_s和snprintf
sprintf_s()是sprintf()的安全版本,通过指定缓冲区长度来避免sprintf()存在的溢出风险 。在使用VS2008时如果你使用了sprintf函数,那么编译器会发出警告:使用sprintf存在风险,建议使用sprintf_s。这个安全版本的原型是:
int sprintf_s(char *buffer,size_t sizeOfBuffer,const char *format [,argument] ... );
不过sprintf_s()是微软私有的函数,考虑到跨平台移植,最好使用snprintf()。两者的原型基本相同:
int snprintf(char *buffer, size_t count, const char *format [,argument] ... );