c - 标准头文件 - std头文件




标准库中的哪些功能必须/应该避免? (9)

应该使用的标准库函数:

SETJMP.H

  • setjmp() 。 与longjmp()一起,这些函数被广泛认可为使用非常危险:它们导致意大利面程序设计,它们带有许多未定义的行为形式,它们会在程序环境中引起意想不到的副作用,例如影响存储在堆栈。 参考文献:MISRA-C:2012规则21.4, CERT C MSC22-C
  • longjmp() 。 请参阅setjmp()

stdio.h中

  • gets() 。 该功能已从C语言中删除(按照C11),因为它按设计不安全。 该功能在C99中已被标记为过时。 使用fgets()代替。 参考文献:ISO 9899:2011 K.3.5.4.1,也见注404)。

stdlib.h中

  • atoi()函数族。 这些没有错误处理,但每当发生错误时调用未定义的行为。 完全多余的函数可以用strtol()函数族来替代。 参考文献:MISRA-C:2012规则21.7。

string.h中

  • strncat() 。 有一个尴尬的界面,经常被滥用。 它主要是一个多余的功能。 另请参阅strncpy()注释。
  • strncpy() 。 这个函数的意图从来不是strcpy()的更安全版本。 它的唯一目的是总是在Unix系统上处理一个古老的字符串格式,并且它包含在标准库中是一个已知的错误。 这个函数是危险的,因为它可能会使字符串没有空终止,并且程序员知道经常使用它不正确。 参考文献: 为什么strlcpy和strlcat认为不安全?

应谨慎使用的标准库函数:

ASSERT.H

  • assert() 。 带有开销,通常不应用于生产代码。 最好使用一个特定于应用程序的错误处理程序来显示错误,但不一定会关闭整个程序。

signal.h中

  • signal() 。 参考文献:MISRA-C:2012规则21.5, CERT C SIG32-C

STDARG.H

  • va_arg()函数族。 C程序中存在可变长度函数几乎总是表示程序设计不佳。 应该避免,除非你有非常具体的要求。

stdio.h中
一般来说, 整个库不建议用于生产代码 ,因为它有许多行为定义不明确和类型安全性差的情况。

  • fflush() 。 完美地用于输出流。 如果用于输入流,则调用未定义的行为。
  • gets_s() 。 C11边界检查界面中包含gets()安全版本。 根据C标准建议,最好使用fgets()代替。 参考文献:ISO 9899:2011 K.3.5.4.1。
  • printf()系列函数。 资源繁重的功能伴随着大量未定义的行为和差的安全性。 sprintf()也有漏洞。 在生产代码中应该避免使用这些函数。 参考文献:MISRA-C:2012规则21.6。
  • scanf()系列函数。 请参阅有关printf() 。 另外,如果使用不正确, scanf()容易出现缓冲区溢出。 在可能的情况下, fgets()首选使用。 参考文献: CERT C INT05-C ,MISRA-C:2012规则21.6。
  • tmpfile()系列函数。 带有各种漏洞问题。 参考文献: CERT C FIO21-C

stdlib.h中

  • malloc()函数族。 在托管系统中使用完美无瑕,但要注意C90中的众所周知的问题,因此不会产生结果malloc()系列函数绝对不能用于独立应用程序。 参考文献:MISRA-C:2012规则21.3。

    还要注意,如果用realloc()的结果覆盖旧指针,则realloc()是危险的。 如果该功能失败,则会产生泄漏。

  • system() 。 有很多开销,虽然可移植,但通常使用系统特定的API函数更好。 伴随着各种不明确的行为。 参考文献: CERT C ENV33-C

string.h中

  • strcat() 。 请参阅strcpy()注释。
  • strcpy() 。 除非要复制的数据的大小未知或大于目标缓冲区,否则使用完美无瑕。 如果没有检查传入的数据大小,可能会出现缓冲区溢出。 这不是strcpy()本身的问题,而是调用应用程序的问题 - strcpy()是不安全的,这大多是微软创造的一个神话
  • strtok() 。 修改调用者字符串并使用内部状态变量,这可能会使其在多线程环境中变得不安全。

我读过堆栈溢出一些C函数是“过时”或“应该避免”。 你能否给我举一些这种功能的例子和原因?

这些功能有哪些替代方案?

我们可以安全地使用它们 - 有什么好的做法?


atoi不是线程安全的。 我使用strtol,而不是从手册页推荐。


不要忘记sprintf - 这是许多问题的原因。 这是事实,因为替代方法,snprintf有时可能会有不同的实现,这会使您的代码变得不可移植。

  1. linux: http://linux.die.net/man/3/snprintf : http://linux.die.net/man/3/snprintf

  2. windows: http://msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx : http://msdn.microsoft.com/en-us/library/2ts7cx93%28VS.71%29.aspx

在情况1(linux)中,返回值是存储整个缓冲区所需的数据量(如果它小于给定缓冲区的大小,则输出被截断)

在情况2(窗口)中,如果输出被截断,返回值是负数。

通常你应该避免不是的功能:

  1. 缓冲区溢出安全(这里已经提到很多功能)

  2. 线程安全/不可重入(例如,strtok)

在每个函数的手册中,您应该搜索关键字,如:安全,同步,异步,线程,缓冲区,错误


人们又一次重复了,曼陀罗式的,令人可笑的断言:“n”版本的str函数是安全版本。

如果那是他们的意图,那么他们总是会终止字符串。

这些函数的“n”版本是为固定长度的字段(比如早期文件系统中的目录条目)而编写的,其中仅当字符串不填充字段时才需要nul终止符。 这也是为什么这些函数具有奇怪的副作用,如果只是作为替换使用时效率低下 - 以strncpy()为例:

如果s2指向的数组是一个比n个字节短的字符串,则将null字节附加到由s1指向的数组中的副本,直到写入所有n字节。

由于分配用于处理文件名的缓冲区通常为4KB,因此可能导致性能大幅下降。

如果你想要“假定”安全的版本,然后获得 - 或者写你自己的strl例程(strlcpy,strlcat等),它们总是终止字符串并且没有副作用。 请注意,虽然这些并不是真的安全,因为它们可以静静地截断字符串 - 这在任何真实世界的程序中都不是最好的行为方式。 在某些情况下,这是可以的,但也有很多情况会导致灾难性后果(例如印出医疗处方)。


可能值得再次重申的是, strncpy()并不是strcpy()的通用替代品,它可能是它的名字。 它专为不需要终结符的固定长度字段而设计(它最初设计用于UNIX目录条目,但可用于诸如加密密钥字段之类的内容)。

但是,使用strncat()作为strcpy()的替代品很容易:

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

if你知道dest_size肯定是非零的话, if测试显然可以放弃。


同时查看微软禁止使用的API列表。 这些API(包括许多已经在此列出的)被禁止使用Microsoft代码,因为它们经常被滥用并导致安全问题。

你可能不同意所有这些,但他们都值得考虑。 当它的误用导致了一些安全漏洞时,它们会向列表中添加一个API。


安全使用scanf非常困难。 使用scanf可以避免缓冲区溢出,但是在读取不符合请求类型的数字时,您仍然容易受到未定义的行为影响。 在大多数情况下, fgets后跟自解析(使用sscanfstrchr等)是更好的选择。

但我不会说“始终避免scanf ”。 scanf有其用途。 作为一个例子,假设您想要读取长度为10个字节的char数组中的用户输入。 你想删除尾随的换行符,如果有的话。 如果用户在换行符之前输入9个以上的字符,则需要将前9个字符存储在缓冲区中,并丢弃所有内容,直到下一个换行符。 你可以做:

char buf[10];
scanf("%9[^\n]%*[^\n]", buf));
getchar();

一旦你习惯了这种习惯用法,它就会更短,并且在某种程度上比以下方面更清洁:

char buf[10];
if (fgets(buf, sizeof buf, stdin) != NULL) {
    char *nl;
    if ((nl = strrchr(buf, '\n')) == NULL) {
        int c;
        while ((c = getchar()) != EOF && c != '\n') {
            ;
        }
    } else {
        *nl = 0;
    }
}

有些人会声称应该避免使用strcpystrcat ,而使用strncpystrncat 。 在我看来,这有点主观。

在处理用户输入时绝对应该避免 - 毫无疑问在这里。

在离用户“远”的代码中,当你知道缓冲区足够长时, strcpystrcat可能会更有效一些,因为计算n传给它们的表兄可能是多余的。


避免

  • 多线程程序的strtok不是线程安全的。
  • 因为它可能会导致缓冲区溢出




obsolete