对C11(N1570草案)的反应,7.24.5.8 strtok功能:
不需要
strtok
功能来避免与其他人的数据竞争 调用strtok
函数。 311)311)可以使用
strtok_s
函数来避免数据争用。
据我了解,这是由于其(全局)内部状态,它保存有关下一个令牌的当前位置的信息。这允许使用它,如下面的习语:
p = strtok(str, delim);
while (p != NULL)
{
puts(p);
p = strtok(NULL, delim);
}
假设strtok_s
(在附件K中定义)不可用,以下用法是否足以进行重入,因为它完全忽略了内部状态?
char str[] = "ab;cd";
char * const endstr = str + strlen(str);
const char *delim = ";";
char *p = strtok(str, delim);
while (p != NULL)
{
puts(p);
if (p + strlen(p) == endstr) // detect ending token
break;
p = strtok(p + strlen(p)+1, delim);
}
第二个想法,可能永远不应该使用这种技术,因为它可能会导致安全问题。由于序列永远不会通过NULL
的最后一次调用完成,因此strtok
的内部状态(由令牌字符串的地址组成)可以在应用程序的剩余生命周期内保留
答案 0 :(得分:2)
以下用法是否足以进行重入,因为它完全忽略了内部状态? [...]
您如何知道您的替代方案是否完全忽略了strtok()
的内部状态?该州的性质和用途的细节没有具体说明。因此,在不参考特定实现的情况下,我们只知道在以NULL
作为其第一个参数再次调用该函数的情况下跟踪下一个标记的开始就足够了。没有什么比这更能说明它是不可靠的了。
现在,我承认似乎可能静态应该只由一个char *
组成,并且当首次传递非null时函数的行为参数不应该取决于该静态的值。我想这就是你所描述的" reentrant"。但是,我不会使用该术语,因为尽管您的方法可能会产生不受其他线程操作影响的结果,但它仍然会影响(并参与数据竞争)其他不遵循相同模式的线程
最后,是的,确实存在涉及strtok()
的数据竞争的可能性。如果两个不同的线程调用strtok()
并且至少有一个传递NULL
作为第一个参数,则两个线程都会修改该函数的静态状态,并且至少有一个线程读取它。 strtok()
不需要以任何特定方式保护其内部状态,因此如果调用不以某种方式从外部同步,则可能存在数据竞争。在那种情况下,程序行为是未定义的。
因此,如果您有理由担心与strtok()
相关的非重入和数据竞争风险,并且您不想依赖strtok_s()
或POSIX' {{ 1}},然后我建议完全避免strtok_r()
。您可以基于strtok()
构建解决方案,而这样的替代方案也可能比您的方法更有效,因为它不需要任何strcspn()
次调用。
答案 1 :(得分:0)
明确使用strtok是否足以使其重入?
如果“explicit”表示“仅使用非空指针调用strtok”,则答案为是。但我不会称它为可重入的。使用非空指针调用strtok
只意味着您重新开始拆分字符串的过程。
在标准(7.24.5.8(2)
中的草案(N1539)中说明序列中的第一个调用具有非空的第一个参数;
但是如果你想绕过strtok
的基本功能,为什么要使用呢?您可以改用strstr
或strchr
。或者 - 正如@chux在评论中所建议的那样 - strcspn
答案 2 :(得分:0)
我有点迟了,但它是可重入的。通常,strtok
有一个内部static
变量,用于保留字符串中的位置。如果strtok
的第一个参数是NULL
,则它引用此内部变量以扫描下一个标记。因此,如果您自己总是提供第一个参数,那么它将起作用,因为您可以根据需要使用strtok
获取尽可能多的单独字符串(在这种情况下,我们传递的字符串恰好是所有较大字符串的子串)。 strtok
将简单地假设您传递了一个新字符串并按预期工作。
请记住,您需要为NULL
的每次调用传递非strtok
参数,才能使您的方法正常工作。或者,我将定义以下函数。
char *my_strtok(char *str, const char *delim, char **saveptr) {
if (*saveptr == NULL)
return NULL;
char *last = (str) ? str + strlen(str) : *saveptr + 1 + strlen(*saveptr + 1);
char *ret = (str) ? strtok(str, delim) : strtok(*saveptr + 1, delim);
*saveptr = (ret) ? ret + strlen(ret) : ret;
*saveptr = (*saveptr != last) ? *saveptr : NULL;
return ret;
}
然后你的代码看起来像这样:
char *str = "ab;cd";
const char *delim = ";";
char *state = str;
char *p = my_strtok(str, delim, &state);
while (p != NULL)
{
puts(p);
p = strtok(NULL, delim, &state);
}
my_strtok
的优点是它也适用于嵌套调用。