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’添加到新字符串。