我正在编写一个获取系统路径环境变量的函数,将每个路径拆分,然后在其他一些额外字符上进行汇总到每条路径的末尾。
在我使用strcat()
函数之前,一切正常(请参阅下面的代码)。
char* prependPath( char* exeName )
{
char* path = getenv("PATH");
char* pathDeepCopy = (char *)malloc(strlen(path) + 1);
char* token[80];
int j, i=0; // used to iterate through array
strcpy(pathDeepCopy, path);
//parse and split
token[0] = strtok(pathDeepCopy, ":"); //get pointer to first token found and store in 0
//place in array
while(token[i]!= NULL) { //ensure a pointer was found
i++;
token[i] = strtok(NULL, ":"); //continue to tokenize the string
}
for(j = 0; j <= i-1; j++) {
strcat(token[j], "/");
//strcat(token[j], exeName);
printf("%s\n", token[j]); //print out all of the tokens
}
}
我的shell输出是这样的(我将“/ which”连接到所有内容上):
...
/usr/local/applic/Maple/bin/which
which/which
/usr/local/applic/opnet/8.1.A.wdmguru/sys/unix/bin/which
which/which
Bus error (core dumped)
我想知道为什么strcat
显示新行然后重复which/which
。
我也想知道最后的Bus error (core dumped)
。
使用strcat()
之前有没有人见过这个?
如果是这样,任何人都知道如何解决它?
由于
答案 0 :(得分:6)
strtok()没有给你一个新的字符串 它通过在分割字符为。
的位置插入char'\ 0'来破坏输入字符串所以你使用strcat(token [j],“/”)会将'/'字符放在'\ 0'所在的位置。
此外,最后一个标记将开始将已分配内存末尾的“哪个”附加到未知内存中。
您可以使用strtok()将字符串拆分为块。但是如果你想在令牌上附加任何东西,你需要制作一个令牌的副本,否则你的追加将溢出到下一个令牌上。
此外,你需要更加小心你的内存分配,你在整个地方泄漏内存: - )
PS。如果必须使用C-Strings。使用strdup()复制字符串。
char* prependPath( char* exeName )
{
char* path = getenv("PATH");
char* pathDeepCopy = strdup(path);
char* token[80];
int j, i; // used to iterate through array
token[0] = strtok(pathDeepCopy, ":");
for(i = 0;(token[i] != NULL) && (i < 80);++i)
{
token[i] = strtok(NULL, ":");
}
for(j = 0; j <= i; ++j)
{
char* tmp = (char*)malloc(strlen(token[j]) + 1 + strlen(exeName) + 1);
strcpy(tmp,token[j]);
strcat(tmp,"/");
strcat(tmp,exeName);
printf("%s\n",tmp); //print out all of the tokens
free(tmp);
}
free(pathDeepCopy);
}
答案 1 :(得分:2)
strtok()标记到位。当您开始向令牌附加字符时,您将覆盖下一个令牌的数据。
另外,一般来说,简单地连接到现有字符串是不安全的,除非您知道字符串所在的缓冲区大小足以容纳结果字符串。这是C程序中的错误的主要原因(包括可怕的缓冲区溢出安全性错误)。
因此,即使strtok()返回与原始字符串无关的全新字符串(它没有),当你连接它们时,你仍然会超出字符串缓冲区。
strcpy()/ strcat()的一些更安全的替代品,你可能想要查看(你可能需要追踪其中一些的实现 - 它们并非都是标准的):
strncat函数()
strlcpy() - 类似于strncpy(),但意图更易于使用且更健壮(http://en.wikipedia.org/wiki/Strlcat)
strlcat提供()
strcpy_s() - 这些函数的Microsoft变体
如果你可以使用C ++,你应该努力使用的API: std :: string class 。如果你使用C ++ std :: string类,你几乎不必担心包含字符串的缓冲区 - 该类为你管理所有这些。
答案 2 :(得分:2)
strtok
不会复制令牌,而只是在字符串中指向它。因此,当你在令牌的末尾捕捉'/'时,你会在 next 令牌的开头写一个'\ 0',或者超过缓冲区的末尾。
另请注意,即使strtok 已经返回令牌的副本而不是原件(它没有),它也不会为你附加额外空间来附加字符,所以它' d仍然是缓冲区溢出错误。
答案 3 :(得分:1)
如果您使用的是C ++,请考虑boost::tokenizer上讨论的here。
如果您遇到C,请考虑使用strtok_r,因为它是可重入且线程安全的。并不是说在这种特殊情况下你需要它,但这是一个很好的习惯。
哦,并使用strdup一步创建重复的字符串。
答案 4 :(得分:1)
好的,首先要小心。你正在失去记忆。 Strtok()返回指向下一个标记的指针,并将其存储在一个字符数组中。 而不是char token [80]它应该是char * token。 使用strtok时要小心。 strtok实际上会破坏名为pathDeepCopy的char数组,因为它会用'\ 0'替换每次出现的“:”。正如Mike F告诉你的那样。 一定要使用calloc的memset初始化pathDeppCopy。 因此,当您编写令牌[i]时,无法知道指向的是什么。 由于令牌中没有数据有效,因此您可能会因为尝试连接而抛出核心转储。另一个没有有效数据(令牌)的字符串。 您正在寻找的Perphaps是指向char的指针数组,其中存储了strtok返回的令牌的所有指针,在这种情况下,令牌将像char * token [];
希望这会有所帮助。
答案 5 :(得分:0)
用
替换它strcpy(pathDeepCopy,path);
//parse and split
token[0] = strtok(pathDeepCopy, ":");//get pointer to first token found and store in 0
//place in array
while(token[i]!= NULL) { //ensure a pointer was found
i++;
token[i] = strtok(NULL, ":"); //continue to tokenize the string
}
// use new array for storing the new tokens
// pardon my C lang skills. IT's been a "while" since I wrote device drivers in C.
const int I = i;
const int MAX_SIZE = MAX_PATH;
char ** newTokens = new char [MAX_PATH][I];
for (int k = 0; k < i; ++k) {
sprintf(newTokens[k], "%s%c", token[j], '/');
printf("%s\n", newtoken[j]); //print out all of the tokens
}
这将取代覆盖内容并阻止核心转储。
答案 6 :(得分:0)
并且不要忘记检查malloc是否返回NULL!