关于C#:为什么strtok()被认为是不安全的?

Why is strtok() Considered Unsafe?

我需要注意的strtok的哪些特性(在缓冲区溢出方面)是不安全的?

对我来说有点奇怪的是,在VisualC++中,EDCOX1 1(它是"安全的")有一个额外的"上下文"参数,但是看起来它在其他方面是相同的。是相同的,还是实际上不同?


根据本文件的strtok_节:

6.7.3.1 The strtok_s function The strtok_s function fixes two problems
in the strtok function:

  • A new parameter, s1max, prevents strtok_s from storing outside of the
    string being tokenized. (The string
    being divided into tokens is both an
    input and output of the function since
    strtok_s stores null characters into
    the string.)
  • A new parameter, ptr, eliminates the static internal state that
    prevents strtok from being re-entrant
    (Subclause 1.1.12). (The ISO/IEC 9899
    function wcstok and the ISO/IEC 9945
    (POSIX) function strtok_r fix this
    problem identically.)

  • 没有什么不安全的。你只需要了解它是如何工作的以及如何使用它。在编写代码和单元测试之后,用valgrind重新运行单元测试只需要几分钟的额外时间,以确保在内存边界内运行。手册上说的都是:

    BUGS

    Be cautious when using these functions. If you do use them, note that:

    • These functions modify their first argument.
    • These functions cannot be used on constant strings.
    • The identity of the delimiting character is lost.
    • The strtok() function uses a static buffer while parsing, so it's not thread safe. Use strtok_r() if this matters to you.


    Stutk在VisualC++中是安全的(但在其他地方),因为它使用线程本地存储来保存调用之间的状态。在其他地方,全局变量用于保存strtok()状态。

    然而,即使在VC++中,strtok是线程安全的,它仍然有点奇怪——不能同时在同一线程中的不同字符串上使用strtok()。例如,这不会很好地工作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
         token = strtok( string, seps );
         while(token)
         {
            printf("token=%s
    "
    , token)
            token2 = strtok(string2, seps);
            while(token2)  
            {
                printf("token2=%s", token2);
                token2 = strtok( NULL, seps );
            }
            token = strtok( NULL, seps );
         }

    它不能很好地工作的原因-对于每个线程,只有一个状态可以保存在线程本地存储中,在这里,第一个字符串和第二个字符串需要两个状态。因此,虽然strtok使用vc++是线程安全的,但它是不可重入的。

    strtok-s(或其他任何地方的strtok-r)所提供的——一个明确的状态,然后strtok就变成可重入状态。


    如果没有正确的以空结尾的字符串,则会导致缓冲区溢出。还要注意(这是我学到的最难的方法)strtok似乎并不关心内部字符串。例如,"hello"/"world"将解析"hello"/"world",而"hello/world"将解析为"hello world"。注意,它在/上拆分,忽略了它在圆括号内的事实。