是什么使C标准库函数变得危险,还有什么替代方法?

是什么使C标准库函数变得危险,还有什么替代方法?,c,function,C,Function,在学习C的过程中,我经常会遇到一些资源,其中建议永远不要使用某些函数(例如,get()),因为它们很难或不可能安全使用 如果C标准库包含许多这样的“从不使用”函数,那么似乎有必要了解它们的列表、是什么使它们不安全以及如何取而代之 到目前为止,我已经了解了以下功能: 无法防止覆盖内存 不保证以null终止字符串 在呼叫之间保持内部状态 通常被认为使用不安全。是否有显示这些行为的功能列表?还有其他类型的函数无法安全使用吗?strtok()通常被认为是有害的,因为它在调用之间存储状态信息。不要尝试

在学习C的过程中,我经常会遇到一些资源,其中建议永远不要使用某些函数(例如,
get()
),因为它们很难或不可能安全使用

如果C标准库包含许多这样的“从不使用”函数,那么似乎有必要了解它们的列表、是什么使它们不安全以及如何取而代之

到目前为止,我已经了解了以下功能:

  • 无法防止覆盖内存
  • 不保证以null终止字符串
  • 在呼叫之间保持内部状态

通常被认为使用不安全。是否有显示这些行为的功能列表?还有其他类型的函数无法安全使用吗?

strtok()
通常被认为是有害的,因为它在调用之间存储状态信息。不要尝试在多线程环境中运行它

不采用最大长度参数,而是依赖于要出现的结束标记的任何函数(例如许多“字符串”处理函数)

在调用之间保持状态的任何方法。

查看第7页(PDF第9页)

编辑:从页面开始-

strcpy系列
strncpy系列
strcat系列
scanf系列
sprintf系列
获取族

  • sprintf
    不正确,不检查大小,使用
    snprintf
  • gmtime
    localtime
    --
    使用gmtime\u r
    localtime\u r

在过去,大多数字符串函数都没有边界检查。当然,他们不能仅仅删除旧函数,或者修改其签名以包含上限,这将破坏兼容性。现在,对于这些函数中的几乎每一个,都有一个可选的“n”版本。例如:

strcpy -> strncpy
strlen -> strnlen
strcmp -> strncmp
strcat -> strncat
strdup -> strndup
sprintf -> snprintf
wcscpy -> wcsncpy
wcslen -> wcsnlen
还有更多

另请参见创建头文件的项目,如果您使用不安全的函数,该头文件会导致gcc报告错误。

是的,
fgets(…,…,STDIN)
gets()
的一个很好的替代方案,因为它使用了一个大小参数(
gets()
实际上已完全从C标准中删除,在C11中)。请注意,
fgets()
并不能完全替代
gets()
,因为前者将包括终止的
\n
字符,前提是缓冲区中有足够的空间来读取完整的行

scanf()
在某些情况下被认为是有问题的,而不是直截了当的“坏”,因为如果输入不符合预期的格式,可能无法进行合理的恢复(它不允许您回放输入并重试)。如果你可以放弃格式不好的输入,它是有用的。这里的“更好”替代方法是使用输入函数,如
fgets()
fgetc()
读取输入块,然后使用
sscanf()
扫描它,或使用字符串处理函数,如
strchr()
strtol()
解析它。有关
scanf()
中的
“%s”
转换说明符的具体问题,请参见下文

它不是一个标准的C函数,但是BSD和POSIX函数
mktemp()
通常不可能安全使用,因为在测试文件是否存在和随后创建文件之间总是存在TOCTTOU竞争条件
mkstemp()
tmpfile()
是很好的替代品

strncpy()
是一个有点棘手的函数,因为如果没有空间,它不会空值终止目标。尽管有明显的通用名称,但此函数的设计目的是创建不同于普通C字符串的特定样式的字符串—存储在已知固定宽度字段中的字符串,如果字符串完全填充字段,则不需要空终止符(原始UNIX目录项就是这种样式)。如果没有这种情况,可能应该避免使用此函数

atoi()
在某些情况下可能是一个不好的选择,因为您无法判断在进行转换时何时出现错误(例如,如果数字超出了
int
的范围)。如果这对您很重要,请使用strtol()

strcpy()
strcat()
sprintf()
遇到与
get()
类似的问题-它们不允许您指定目标缓冲区的大小。至少在理论上,安全地使用它们仍然是可能的,但使用
strncat()
snprintf()
(您可以使用
strncpy()
,但请参见上文)要好得多。请注意,
snprintf()
n
是目标缓冲区的大小,而
strncat()
n
是要附加的最大字符数,不包括空终止符。如果您已经计算了相关的字符串和缓冲区大小,另一种选择是
memmove()
memcpy()


在同一主题上,如果使用
scanf()
函数系列,请不要使用普通的
“%s”
-再次指定目标的大小,例如
“%200s”
strcpy

大多数人都同意strcpy是危险的,但strncpy很少是有用的替代品。在任何情况下,知道何时需要截断字符串通常都很重要,因此,通常需要检查源字符串的长度。如果是这种情况,通常memcpy是更好的替代品,因为您确切地知道要复制多少个字符

e、 g.截断是错误:

n = strlen( src );

if( n >= buflen )
    return ERROR;

memcpy( dst, src, n + 1 );
允许截断,但必须返回字符数,以便调用方知道:

n = strlen( src );

if( n >= buflen )
    n = buflen - 1;

memcpy( dst, src, n );
dst[n] = '\0';

return n;

严格地说,有一个真正危险的功能。它是
get()
,因为
n = DST_BUFFER_SIZE;
if ((dst != NULL) && (src != NULL) && (strlen(dst)+strlen(src)+1 <= n))
{
    strcpy(dst, src);
}
strncpy(dst, src, n);
if (n > 0)
{
    dst[n-1] = '\0';
}
char buff[1000];
strncpy(buff, "1", sizeof buff);