循环内的分段故障

时间:2012-02-07 16:32:57

标签: c pointers

我是C#man,C语言的新手第一次使用积分。

此功能可在将来与malloc()realloc()free()配合使用:

char ** split(char * delimiter, char * input) {

        int i = 0;
        int size = sizeof(char *);
        char ** tokens;
        char * token;
        char * state; 
        tokens = (char **) malloc(size);

        if(tokens == NULL) {
            printf("Allocation failed.");
            return;
        }

        for(token = strtok_r(input, delimiter, &state); 
             token != NULL;
             token =  strtok_r(NULL, delimiter, &state),
             i++, size *= i) {

             tokens = (char **) realloc(tokens, size);

             if(tokens == NULL) {
                printf("Realloc failed.");
                return;
             }

             tokens[i] = state;
        }

        return tokens;
}

我打电话的时候:

char * IPNumber = "127.0.01";
char * delimiter = "."; 
char ** parts = split(delimiter, IPNumber);

它提供了segmentation fault

我正在寻找解释如何获取(计算)realloc()函数的第二个参数中使用的大小值的解释。提前致谢。

4 个答案:

答案 0 :(得分:2)

你的malloc / calloc的大小是错误的(你乘以预期的数量,这使得数组增长count!

在第一项上:i = 0,size = sizeof(char *); 在第二项i = 1,size = sizeof(char)/ *对于两个元素来说太小* /

char ** split(char * delimiter, char * input) {

        unsigned size , used;
        char ** array = NULL;
        char * token;
        char * state; 

        size = used = 0;
        for(token=strtok_r(input, delimiter, &state); token; token=strtok_r(NULL, delimiter, &state) ) {

             if (used+1 >= size) {
                        size = size ? 2*size: 4;
                        array = realloc(array, size * sizeof *array);

                        if (!array) { printf("Realloc failed."); return NULL ; /*leak here*/ }
             }

             array[used++] = state;
        }
        /* NOTE: need a way to communicate the number of elements back to the caller */
        if (array) array[used] = NULL;

        return array;
}

更新:这是一个测试驱动程序

int main(void)
{
char stuff[] = "this is the stuff";
char **ppp;
unsigned idx;

ppp = split( " " , stuff);
for (idx = 0; ppp && ppp[idx]; idx++) {
        fprintf(stdout, "%u: %s\n", idx, ppp[idx] );
        }
return 0;
}

答案 1 :(得分:2)

好的,我猜想你想要的是返回一个字符串数组:

包括

char ** split(char * delimiter, char * input) {
        int i;
        char ** tokens;
        char * token;
        char * state;

        tokens = (char **) malloc(sizeof(char *) * (2));

        if(tokens == NULL) {
            printf("Allocation failed.");
            return NULL;
        }

        tokens[0]=(char *)1; /* one element populated */
        tokens[1]=NULL; /* no tokens */

        for(i=1, token = strtok_r(input, delimiter, &state);
             token != NULL;
             token =  strtok_r(NULL, delimiter, &state),
             i++) {

             /* grow array by one element - originally made with 2 */
             {
               char **new =(char **) realloc(tokens, (i+2) * sizeof(char *));

                 if(new == NULL) {
                    printf("Realloc failed.");
                    free(tokens);
                    return NULL;
                 }
                 else
                 {
                    tokens = new;
                    tokens[i+1] = NULL;  /* initialize new entry */
                 }
             }

             tokens[i] = token;
             tokens[0] = (char *)i;
        }

        return tokens;
}

int main( void )
{
    char str[] = "129.128.0.1";
    char delim[] = ".";
    char **ret;

    ret = split( delim, str );

    printf( "tokens = %d\n", (int)ret[0] );
    printf( "tokens[1] = %s\n", ret[1] );
    printf( "tokens[2] = %s\n", ret[2] );
    printf( "tokens[3] = %s\n", ret[3] );
    printf( "tokens[4] = %s\n", ret[4] );
    printf( "tokens[5] = %s\n", ret[5] );
}
  1. 返回显式值,而不是垃圾。
  2. 更改realloc功能。在每个循环中,您将数组增加一个元素。
  3. 修复内存泄漏
  4. 保存strtok_r返回的值,而不是其私有内部状态变量。
  5. 数组比它需要的大一个,所以确保它被初始化为NULL
  6. 数组的入口零是大小,除非您正在处理 HUGE 字符串
  7. ,否则不应该溢出

答案 2 :(得分:0)

完成重写。发布的原始代码存在一些问题。

  • 重新分配大小计算不正确。
  • 将字符串常量传递给strtok_r无效。它修改了第一个参数,因此在传递字符串文字时可能会导致访问冲突。
  • 将令牌分配到结果数组中的位置从1开始,而不是0。
  • 赋值使用状态变量而不是令牌(可能根本不是所需的结果,可能是未定义的行为)。
  • 调用者无法知道返回数组中有多少个令牌。
  • 对realloc的调用失败不会释放原始指针,因此会泄漏。

因此,我不会尝试描述更改,而是按照与其他模式相同的模式,并根据最大可能的令牌数量显示可能是一个更简洁的实现。

char ** split(char * delimiter, char * input) {
    int size;
    int maxsize;
    char ** tokens;
    char * token;
    char * state;

    // compute max possible tokens, which is half the input length.
    // Add 1 for the case of odd strlen result and another +1 for
    // a NULL entry on the end
    maxsize = strlen( input ) / 2 + 2;
    tokens = (char**)malloc( maxsize * sizeof( char*) );
    if(tokens == NULL) {
        printf("Allocation failed.");
        return NULL;
    }

    size = 0;
    for(token = strtok_r(input, delimiter, &state);
         token != NULL;
         token =  strtok_r(NULL, delimiter, &state) ) {

         tokens[size++] = token;
    }

    assert( size < maxsize );
    // Put a NULL in the last entry so the caller knows how many entries
    // otherwise some integer value would need to be returned as an output
    // parameter.
    tokens[size] = NULL;

    // NOTE: could use realloc from maxsize down to size if desired

    return tokens;
}

用法可能如下所示。注意使用strdup来避免将字符串常量传递给函数:

char * IPNumber = strdup( "127.0.01" );
char * delimiter = ".";
char ** parts = split(delimiter, IPNumber);
int i;

if ( parts ) {
   for ( i = 0; parts[i] != NULL; i++ )
      printf( "%s\n", parts[i] );

   free( parts );
   }

free( IPNumber );

答案 3 :(得分:0)

我打算指出需要解决的问题,但只需按照以下步骤重写:

char **split(char *delim, char *input)
{
  char *save;  /* saved state for strtok_r */
  char **tmp,  /* temporary result from realloc (for error handling) */
       **res;  /* result - NULL-terminated array of tokens */
  int i,       /* index of current/last token */ 
      count;   /* number of elements in res (including NULL) */

  /* Allocate first element for res */
  if ( !(res = malloc(sizeof(res[0]))) ) {
    /* return NULL if malloc() fails */
    fprintf(stderr,"split(): malloc() failed\n");
    return NULL;
  }

  /* res[0] = first token, or NULL */
  res[0] = strtok_r(input,delim,&save);

  /* if it was a token, grab the rest.  Last one will be the NULL
   *  returned from strtok_r() */
  if (res[0])
    i = 0;
    count = 1;
    do {
      /* Resize res, for next token */
      /* use a temporary pointer for realloc()'s result, so that
       *  we can check for failure without losing the old pointer */
      if ( tmp = realloc(res, sizeof(res[0]) * ++count) )
        res = tmp;
      else {
        /* if realloc() fails, free res and return NULL */
        free(res);
        fprintf(stderr,"split(): realloc() failed.\n");
        return NULL;
      }
      /* get next token, or NULL */
      res[++i] = strtok_r(NULL,delim,&save);
    } while (res[i]); /* done when last item was NULL */

  return res;
}

因此realloc的大小是所需元素的数量,乘以元素的大小。

以上版本的代码返回以NULL结尾的数组。另一种方法是以某种方式返回数组元素的数量(例如通过int *size_t *参数);但无论如何,你需要一种让调用者知道结果数组结束位置的方法。

使用strtok_r()也会增加另一个问题:原始input字符串保持不变。因此,在使用此(或原始)函数时,您需要牢记这一点 - 当您不需要保留原始字符串时使用它,或者首先复制原始字符串。