【问题标题】:strcat() new line, duplicate stringstrcat() 换行,重复字符串
【发布时间】:2010-09-15 05:43:39
【问题描述】:

我正在编写一个函数,它获取系统的路径环境变量,拆分每个路径,然后将一些其他额外字符连接到每个路径的末尾。

在我使用 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() 时见过这个? 如果是这样,有人知道如何解决吗?

谢谢

【问题讨论】:

  • 是我还是 SO 上有很多未注册的史蒂夫?
  • 您的代码缺少一些“”符号:char pathDeepCopy = (char )malloc(strlen(path) + 1);字符令牌[80];
  • @bk1e:OP 确实输入了它们,但 SO 将它们解释为斜体格式。

标签: c string


【解决方案1】:

strtok() 不会给你一个新的字符串。
它通过在分割字符所在的位置插入 char '\0' 来破坏输入字符串。

因此,您使用 strcat(token[j],"/") 会将 '/' 字符放在 '\0' 所在的位置。
此外,最后一个标记将开始将分配内存末尾的“哪个”附加到未知内存中。

您可以使用 strtok() 将字符串拆分为块。但是,如果您想将任何内容附加到令牌上,则需要制作令牌的副本,否则您的附加内容将溢出到下一个令牌上。

此外,您需要更加注意内存分配,因为您到处都在泄漏内存:-)

PS。如果您必须使用 C 字符串。使用 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);
}

【讨论】:

  • 请注意,strdup() 不是标准 C 的一部分,尽管它是 POSIX 的一部分。
【解决方案2】:

别忘了检查 malloc 是否返回 NULL!

【讨论】:

    【解决方案3】:

    strtok() 就地标记。当您开始将字符附加到标记时,您将覆盖下一个标记的数据。

    此外,一般来说,简单地连接到现有字符串是不安全的,除非您知道字符串所在的缓冲区大小足以容纳结果字符串。这是 C 程序中出现错误的主要原因(包括可怕的缓冲区溢出安全错误)。

    因此,即使 strtok() 返回与您的原始字符串无关的全新字符串(它没有),当您连接到它们时,您仍然会超出字符串缓冲区。

    您可能想要研究一些更安全的 strcpy()/strcat() 替代方案(您可能需要跟踪其中一些的实现 - 它们并不都是标准的):

    • strncpy() - 包括目标缓冲区大小以避免溢出。有不总是终止结果字符串的缺点
    • strncat()

    • strlcpy() - 类似于 strncpy(),但更易于使用且更健壮 (http://en.wikipedia.org/wiki/Strlcat)

    • strlcat()

    • strcpy_s() - 这些函数的 Microsoft 变体

    • strncat_s()

    如果你可以使用 C++,你应该努力使用的 API:std::string 类。如果您使用 C++ std::string 类,您几乎不必担心包含字符串的缓冲区 - 该类会为您管理所有这些。

    【讨论】:

      【解决方案4】:

      如果您使用的是 C++,请考虑 boost::tokenizer,正如在 here 中讨论的那样。

      如果您陷入 C 语言,请考虑使用 strtok_r,因为它是可重入且线程安全的。并不是说在这种特定情况下您需要它,但这是一个养成的好习惯。

      哦,使用 strdup 一步创建您的重复字符串。

      【讨论】:

        【解决方案5】:

        好的,首先,要小心。你正在失去记忆。 Strtok() 返回一个指向下一个标记的指针,您将它存储在一个字符数组中。 而不是 char token[80] 它应该是 char *token。 使用 strtok 时也要小心。 strtok 实际上破坏了名为 pathDeepCopy 的字符数组,因为它会将每次出现的“:”替换为 '\0'。正如 Mike F 上面告诉你的那样。 请务必使用 calloc 的 memset 初始化 pathDeppCopy。 因此,当您编码 token[i] 时,无法知道所指向的内容。 并且由于令牌中没有有效数据,因此很可能会抛出核心转储,因为您正在尝试连接。一个字符串到另一个没有验证数据(令牌)的字符串。 您正在寻找的可能是指向 char 的指针数组,其中存储指向 strtok 返回的令牌的所有指针,在这种情况下,令牌将类似于 char *token[];

        希望这会有所帮助。

        【讨论】:

        • 我认为 'token' 实际上是一个 char * [80],但 SO 将 '*' 解释为斜体格式。
        【解决方案6】:

        替换为

        strcpy(pathDeepCopy, 路径);

          //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
        }
        

        这将取代覆盖内容并防止核心转储。

        【讨论】:

          【解决方案7】:

          strtok 不会复制令牌,而只是在字符串中指向它。因此,当您将“/”添加到标记的末尾时,您将在 next 标记的开头或缓冲区的末尾写入“\0”。

          还请注意,即使 strtok 确实返回了令牌的副本而不是原件(它没有),它也不会为您分配额外的空间来附加字符,所以它' d 仍然是缓冲区溢出错误。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-12-18
            • 2020-02-14
            • 2014-09-23
            • 2013-06-04
            • 1970-01-01
            • 1970-01-01
            • 2015-10-22
            • 2013-05-12
            相关资源
            最近更新 更多