我过去几个月一直在做一个高级项目,我们团队开发过程中的一个主要问题是处理Visual-C ++和gcc之间的分歧。 (是的,我知道我们都应该拥有相同的开发环境。)事情就此完成了,但是我今天碰到了一个温和的bug,让我想知道Visual-C ++对于新手是否更容易(像我一样)按设计。
在我的一个标题中,有一个函数依赖于strtok
来剪切一个字符串,进行一些比较并返回一个类似格式的字符串。它的工作原理如下:
int main()
{
string a, b, c;
//Do stuff with a and b.
c = get_string(a,b);
}
string get_string(string a, string b)
{
const char * a_ch, b_ch;
a_ch = strtok(a.c_str(),",");
b_ch = strtok(b.c_str(),",");
}
strtok
因为在标记化方面表现出色而臭名昭着,但在摧毁原始字符串以进行标记化方面同样出色。因此,当我使用gcc编译它并试图对a
或b
做任何事情时,我得到了意外的行为,因为在字符串中完全删除了使用的分隔符。这是一个我不清楚的例子;如果我设置a = "Jim,Bob,Mary"
和b="Grace,Soo,Hyun"
,则会将其定义为a="JimBobMary"
和b="GraceSooHyun"
,而不是像我想要的那样保持不变。
然而,当我在Visual C ++下编译它时,我找回原始字符串并且程序执行正常。
我尝试动态地为字符串分配内存并以“标准”方式复制它们,但唯一有效的方法是使用malloc()
和free()
,我听说在C ++中不鼓励这样做。虽然我对此感到好奇,但我真正的问题是:为什么程序在用VC ++编译时能够工作,而不是用gcc编写?
(这是我在尝试使代码跨平台时遇到的众多冲突之一。)
提前致谢!
-Carlos Nunez
答案 0 :(得分:6)
这是未定义行为的示例。您将string::c_str()
的结果const char*
传递给strtok,其中char*
为char *ap = strdup(a.c_str());
const char *a_ch = strtok(ap, ",");
/* do whatever it is you do */
free(ap);
。通过修改std :: string数据的内容,您将调用未定义的行为(除非您正在进行转换,否则应该收到警告)。
你什么时候检查a和b的值?在get_string中,还是在main中? get_string传递了a和b的副本,因此strtok很可能不会改变main中的原件。但是,它可以,因为您正在调用未定义的行为。
执行此操作的“正确方法”是使用malloc / free或new [] / delete []。你正在使用C函数,所以你已经犯了与使用malloc / free相同的犯罪行为。一种相对优雅但安全的方法是:
{{1}}
另外请记住,strtok使用全局状态,因此它不能很好地与线程一起使用。
答案 1 :(得分:3)
标记将由函数strtok
自动替换为空字符。这不是你可以用常数数据做的事情。
为了使您的代码安全且跨平台,请考虑使用boost::tokenizer
。
答案 2 :(得分:0)
我认为代码正在运行,因为字符串实现存在差异。将VC ++字符串实现传递给可能修改字符串的函数时,VC ++字符串实现必须进行复制。