strcat vs strncat – 什么时候应该使用哪个函数?
一些静态代码分析器工具建议为了安全起见,所有strcat用法都应该替换为strncat?
在程序中,如果我们清楚地知道目标缓冲区和源缓冲区的大小,是否仍然建议使用strncat?
另外,根据静态工具的建议,是否应该使用strcat?
如果您完全确定源缓冲区的大小并且源缓冲区包含终止字符串的NULL字符,那么当目标缓冲区足够大时,您可以安全地使用strcat。
我仍然建议使用strncat并给它目标缓冲区的大小 – 目标字符串的长度 – 1
注意:我编辑了这个,因为评论指出我之前的回答非常错误。
将两个字符串连接成一个字符串。
原型
#include char * strcat(char *restrict s1, const char *restrict s2); char * strncat(char *restrict s1, const char *restrict s2, size_t n);
描述
strcat()
和strncat()
函数strncat()
null结尾的字符串s2的副本附加到以null结尾的字符串s1的末尾,然后添加一个终止的\ 0’。 字符串s1必须有足够的空间来保存结果。
strncat()函数从s2追加不超过n个字符,然后添加一个终止\ 0’。
源字符串和目标字符串不应重叠,因为行为未定义。
返回值
The `strcat()` and `strncat()` functions return the pointer s1.
安全考虑
strcat()
函数很容易被滥用,使恶意用户能够通过缓冲区溢出攻击任意改变正在运行的程序的function。
避免使用strcat()
。 相反,使用strncat()
或strlcat()
并确保不再将其复制到目标缓冲区中的字符数。
请注意, strncat()
也可能存在问题。 根本要截断字符串可能是一个安全问题。 由于截断的字符串不会与原始字符串一样长,因此它可能指的是完全不同的资源,截断资源的使用可能导致非常不正确的行为。 例:
void foo(const char *arbitrary_string) { char onstack[8] = ""; #if defined(BAD) /* * This first strcat is bad behavior. Do not use strcat! */ (void)strcat(onstack, arbitrary_string); /* BAD! */ #elif defined(BETTER) /* * The following two lines demonstrate better use of * strncat(). */ (void)strncat(onstack, arbitrary_string, sizeof(onstack) - strlen(onstack) - 1); #elif defined(BEST) /* * These lines are even more robust due to testing for * truncation. */ if (strlen(arbitrary_string) + 1 > sizeof(onstack) - strlen(onstack)) err(1, "onstack would be truncated"); (void)strncat(onstack, arbitrary_string, sizeof(onstack) - strlen(onstack) - 1); #endif }
例
char dest[20] = "Hello"; char *src = ", World!"; char numbers[] = "12345678"; printf("dest before strcat: \"%s\"\n", dest); // "Hello" strcat(dest, src); printf("dest after strcat: \"%s\"\n", dest); // "Hello, World!" strncat(dest, numbers, 3); // strcat first 3 chars of numbers printf("dest after strncat: \"%s\"\n", dest); // "Hello, World!123"
他们不做同样的事情,所以他们不能互相替代。 两者都有不同的数据模型。
-
strcat
的字符串是一个以空字符结尾的字符串,您(作为程序员)保证它有足够的空间。 -
strncat
的字符串是一个char
序列,它以指示的长度终止,如果它应该短于该长度,则终止于空终止。
因此,这些函数的使用仅取决于您可能(或想要)对数据做出的假设。
静态工具通常很难理解使用函数的情况。 我打赌他们中的大多数只是警告每个遇到的strcat而不是实际查看传递给函数的数据是否是确定性的。 如前所述,如果您控制输入数据,则function都不安全。
虽然请注意strncat()稍慢,因为它必须检查’\ 0’终止和计数器,并且还明确地将它添加到结尾。 另一方面,strcat()只检查’\ 0’,并通过从第二个字符串复制终结符以及所有数据,将尾部’\ 0’添加到新字符串。