拆分字符串的功能有时会产生分段错误

时间:2017-11-02 19:34:36

标签: c string split segmentation-fault

我有以下功能来分割字符串。大部分时间它工作正常,但有时会随机导致分段错误。

char** splitString(char* string, char* delim){
    int count = 0;
    char** split = NULL;
    char* temp = strtok(string, delim);

    while(temp){
        split = realloc(split, sizeof(char*) * ++count);

        split[count - 1] = temp;
        temp = strtok(NULL, " ");
    }

    int i = 0;
    while(split[i]){
        printf("%s\n", split[i]);
        i++;
    }

    split[count - 1][strlen(split[count - 1]) - 1] = '\0';
    return split;
}

3 个答案:

答案 0 :(得分:0)

split[count - 1][strlen(split[count - 1]) - 1] = '\0';

应该看起来像

split[count - 1] = NULL;

您没有分配任何内容,以便您可以访问它并输入'\ 0'。

之后将该行放在while(split[i])之前,以便while可以在达到NULL时停止。

答案 1 :(得分:0)

函数strtok不可重入,使用strtok_r()函数这是一个可重入的版本strtok()。

答案 2 :(得分:0)

你有许多微妙的问题,如果你传递一个字符串文字,那么你的函数将会发生段错误。您需要复制要分割的字符串,因为strtok修改了字符串。如果传递字符串文字(存储在只读存储器中),除非已将string声明为const char *string;

,否则编译器无法发出警告

要避免这些问题,只需复制您将要标记的字符串。这样,无论您传递给函数的字符串是如何声明的,都可以完全避免这个问题。

您还应该将指向size_t的指针作为参数传递给您的函数,以便在调用函数中使令牌的数量可用。这样您就不必将 sentinel NULL 作为指向指向您返回的char的指针的最终指针。只需传递一个指针并更新它,以反映在函数中解析的标记数。

将这些碎片放在一起,并稍微清理一下,你可以使用以下方法来做你想做的事情:

char **splitstr (const char *str, char *delim, size_t *n)
{
    char *cpy = strdup (str), *p = cpy; /* copy of str & pointer */
    char **split = NULL;                /* pointer to pointer to char */
    *n = 0;                             /* zero 'n' */

    for (p = strtok (p, delim); p; p = strtok (NULL, delim)) {
        void *tmp = realloc (split, sizeof *split * (*n + 1));
        if (!tmp) { /* validate realloc succeeded */
            fprintf (stderr, "splitstr() error: memory exhausted.\n");
            break;
        }
        split = tmp;                /* assign tmp to split */
        split[(*n)++] = strdup (p); /* allocate/copy to split[n] */
    }
    free (cpy);     /* free cpy */
    return split;   /* return split */
}

添加一个简短的示例程序,您可以执行以下操作:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char **splitstr (const char *str, char *delim, size_t *n)
{
    char *cpy = strdup (str), *p = cpy; /* copy of str & pointer */
    char **split = NULL;                /* pointer to pointer to char */
    *n = 0;                             /* zero 'n' */

    for (p = strtok (p, delim); p; p = strtok (NULL, delim)) {
        void *tmp = realloc (split, sizeof *split * (*n + 1));
        if (!tmp) { /* validate realloc succeeded */
            fprintf (stderr, "splitstr() error: memory exhausted.\n");
            break;
        }
        split = tmp;                /* assign tmp to split */
        split[(*n)++] = strdup (p); /* allocate/copy to split[n] */
    }
    free (cpy);     /* free cpy */
    return split;   /* return split */
}

int main (void) {

    size_t n = 0;                   /* number of strings */
    char *s = "My dog has fleas.",  /* string to split */
        *delim = " .\n",            /* delims */
        **strings = splitstr (s, delim, &n);    /* split s */

    for (size_t i = 0; i < n; i++) {    /* output results */
        printf ("strings[%zu] : %s\n", i, strings[i]);
        free (strings[i]);          /* free string */
    }
    free (strings);     /* free pointers */

    return 0;
}

示例使用/输出

$ ./bin/splitstrtok
strings[0] : My
strings[1] : dog
strings[2] : has
strings[3] : fleas

内存使用/错误检查

在你编写的动态分配内存的任何代码中,你有2个职责关于任何分配的内存块:(1)总是保留一个指向起始地址的指针内存块,(2)当不再需要时,它可以释放

您必须使用内存错误检查程序,以确保您不会尝试在已分配的内存块的范围之外/之外进行写入,尝试读取或基于未初始化值的条件跳转,最后,确认您释放了已分配的所有内存。

对于Linux valgrind是正常的选择。每个平台都有类似的记忆检查器。它们都很简单易用,只需通过它运行程序即可。

$ valgrind ./bin/splitstrtok
==14471== Memcheck, a memory error detector
==14471== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==14471== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==14471== Command: ./bin/splitstrtok
==14471==
strings[0] : My
strings[1] : dog
strings[2] : has
strings[3] : fleas
==14471==
==14471== HEAP SUMMARY:
==14471==     in use at exit: 0 bytes in 0 blocks
==14471==   total heap usage: 9 allocs, 9 frees, 115 bytes allocated
==14471==
==14471== All heap blocks were freed -- no leaks are possible
==14471==
==14471== For counts of detected and suppressed errors, rerun with: -v
==14471== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存并且没有内存错误。

仔细看看,如果您有其他问题,请告诉我。