在将char数组拆分为2D数组的程序上出现段错误

时间:2016-12-04 18:35:52

标签: c arrays segmentation-fault

我编写了一个程序,将char数组拆分为2D数组,作为函数状态定义下面的注释。但是,我在这段代码上收到了分段错误。有人可以帮忙找到原因吗?

my_strlen(str)函数的工作方式与原始strlen(str)函数的工作方式相同,并且完美无缺。并且char数组的长度是有限的,所以我并不担心内存分配的效率。

char **my_str2vect(char *str) {
    // Takes a string 
    // Allocates a new vector (array of string ended by a NULL), 
    // Splits apart the input string x at each space character 
    // Returns the newly allocated array of strings
    // Any number of ' ','\t', and '\n's can separate words.
    // I.e. "hello \t\t\n class,\nhow are you?" -> {"hello", "class,", "how", "are","you?", NULL}
    int str_len = my_strlen(str);
    char **output = malloc(sizeof(char *) * str_len); // Allocate a 2D array first.
    if (**output) {
        for (int a = 0; a < str_len; ++a) {
            output[a] = malloc(sizeof(char) * str_len);
        }
    } else {
        return NULL;
    }
    int i = 0;
    int j = 0;
    while (i < str_len) { // Put the characters into the 2D array.
        int k = 0;
        while ((str[i] == ' ' || str[i] == '\t' || str[i] == '\n') && i < str_len) {
            ++i;
        }
        while ((!(str[i] == ' ' || str[i] == '\t' || str[i] == '\n')) && i < str_len) {
            output[j][k] = str[i];
            ++i;
            ++k;
        }
        output[j][k] = '\0';
        ++j;
    }
    output[j] = NULL;
    return output;
}

3 个答案:

答案 0 :(得分:1)

更正代码更改if (**output)if (output)

我认为你的实现不是内存效率高,而且更优雅 你分配的内存太多了。我试着在代码中解释输出char指针大小的上限。如果您想要具有确切的大小,则必须计算字符串中的单词。这样做可能会更好,但对于练习,我认为我们可以采用更简单的方式。

关于你的代码,我只能说:

  • 我没有在任何地方看到字符串'\0'的结尾,这是一个不好的信号
  • 我没有看到任何字符串副本,这也是一个不好的标志
  • 我没有看到你使用标准库,这通常会让你重新发明轮子

请参阅下面的改进实施(我使用标准C89):

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

char** my_str2vect(char* s) {
    // Takes a string 
    // Allocates a new vector (array of string ended by a NULL), 
    // Splits apart the input string x at each space character 
    // Returns the newly allocated array of strings
    // Any number of ' ','\t', and '\n's can separate words.
    // I.e. "hello \t\t\n class,\nhow are you?" -> {"hello", "class,", "how", "are","you?", NULL}

    int s_size = strlen(s);
    /*
     * size of output is 1 if string contains non delimiters only
     * size of output is 0 if string contains delimiters only
     * size of output is strlen / 2 if string contains ...
     * ...alternation of delimiter and non delimiter, and that is the max size
     * so we allocate that size (upper bound)
     */
    int max_output_size = (s_size / 2) + 1;
    char **output = (char **) malloc(sizeof (char *) * max_output_size);

    //initialize to NULL for convenience
    int k;
    for (k = 0; k < max_output_size; k++)
        output[k] = NULL;

    //work on a copy of s
    char *str = (char *) malloc(s_size + 1);
    strcpy(str, s);

    //pointer for token and delimiters
    char *ptr;
    char delimiter[] = "\n\t ";

    //initialize and create first token
    ptr = strtok(str, delimiter);

    //
    int i = 0;
    while (ptr != NULL) {
        //allocate memory and copy token
        output[i] = malloc(sizeof (char) * strlen(ptr) + 1);
        strcpy(output[i], ptr);
        //get next token
        ptr = strtok(NULL, delimiter);
        //increment
        i++;
    }

    return output;
}

int main(int argc, char *argv[]) {

    char **result = my_str2vect("hello \t\t\n class,\nhow are you?");

    int i;
    for (i = 0; result[i] != NULL; i++)
        printf("%s\n", result[i]);

    return 0;
}

答案 1 :(得分:1)

我尝试使用gdb来确定问题。 enter image description here 它约为**output控制。您应该检查*output的地址,而不是指向指针的位置。您将在for循环中分配位置,直到字符串的长度。 可能会导致碎片整理。此外,1D char数组应由const 传递为不可更改。相反,您应该使用代码段

// allocation (in the function)
// protoype: char** my_str2vect(char const* str)
int a;
char** output = malloc(str_len * sizeof(char *));
    output[0] = malloc(str_len * str_len * sizeof(char));
    for(a = 1; a < str_len; a++)
        output[a] = output[0] + a * str_len;

// freeing (in main())  
char ** x;
char const* str = "hello \t\t\n class,\nhow are you?";
x = my_str2vect(str);

free((void *)x[0]);
free((void *)x);

En passant,the source有助于更多地了解分配。

答案 2 :(得分:1)

调试器告诉你if (**output)已损坏。它试图取消引用第一个输出数组位置中的指针。这是if点上的垃圾。因此,seg错误。你想要if (output)。当我修复此问题并使用strlen代替您的重写时,它似乎工作正常。

制作输入字符串的一个副本并将其用于返回向量中的所有字符串要简单得多。您也可以使用strtok来查找单词,但这不是线程安全的。

这是一个建议:

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

char **split(char *s_org) {
  size_t i;
  // Skip initial whitespace, then copy everything else.
  for (i = 0; s_org[i] && isspace(s_org[i]); ++i) /* skip */;
  char *s = strdup(s_org + i);
  size_t n_rtn = 0, size = 0;
  char **rtn = malloc(sizeof *rtn);
  for (i = 0;;) {
    if (!s[i]) {
      rtn[n_rtn] = NULL;
      return realloc(rtn, (n_rtn + 1) * sizeof *rtn);
    }
    if (n_rtn == size) {
      size = 2 * size + 1;
      rtn = realloc(rtn, size * sizeof *rtn);
    }
    rtn[n_rtn++] = s + i;
    while (s[i] && !isspace(s[i])) ++i;
    if (s[i]) {
      s[i++] = '\0';
      while (isspace(s[i])) ++i;
    }
  }
}

int main(void) {
  char **rtn = split("  hello \t\t\n class,\nhow are you?");
  for (char **p = rtn; *p; ++p)
    printf("%s\n", *p);
  // Freeing the first element frees all strings (or does nothing if none)
  free(rtn[0]);
  free(rtn);
  return 0;
}

这省略了对NULLmalloc的{​​{1}}返回的检查。但它们很容易添加。

您询问了代码中的“其他问题”。我在这里修了一些:

  • 使用realloc索引数组。
  • 根据需要增加输出数组。这真的不是那么难......
  • 避免对size_t进行不必要的调用。
  • 当简单检查终止malloc时,请避免使用strlen
  • 使用惯用语NULL分配FOO *p = malloc(sizeof *p);。它比FOO更不容易出错。