2012-02-07 48 views
2

我是C#人,第一次使用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()函數的第二個參數中使用的大小值。提前致謝。

+3

如果你解釋你正在嘗試做什麼,你會得到更好的答案。 – IanGilham 2012-02-07 16:36:05

+0

你是怎麼調用這個函數的? – cnicutar 2012-02-07 16:40:26

+0

IanGilham,cnicutar查看我的編輯 – 2012-02-07 16:55:54

回答

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); 
+0

感謝您的回覆。我嘗試在賦值'tokens [i] = token;之前增加'i ++'的循環次數,並按照你的說法進行計算。但它解決了不是我的問題,我得到同樣的錯誤。 – 2012-02-07 17:42:59

+0

@TheMask:我可能不清楚如何將增量移出循環。在這種情況下,它需要位於循環的底部。如果在賦值之前增加它,它將跳過數組中的第一個位置,並仍然覆蓋內存。我會編輯並嘗試澄清。 – 2012-02-07 17:47:18

2

你的malloc /釋放calloc的大小是錯誤的(你乘以預期的數量,這使得該陣列由count!增長)

第一項:i = 0,size = sizeof(char *); 在第二項目i = 1,大小=的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; 
} 

UPDATE太小:這裏是一個測試驅動器

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; 
} 
+0

它在你的電腦上工作? – 2012-02-07 17:22:53

+0

是的,它可以工作(即:不會崩潰)。它跳過第一個元素,並在末尾添加一個指向空字符串的額外元素。但它不會崩潰。順便說一句:你調用它的方式,*輸入指向一個只讀字符串「127.0.0.1」。這是行不通的,strtok()想要可寫的字符串。 – wildplasser 2012-02-07 17:32:58

+0

你的平臺和編譯器是什麼?它不適用於我,在:Ubuntu的10.4,GCC 4.4.3。 – 2012-02-07 17:35:33

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. 陣列進入零是大小,它不應該溢出,除非你正在處理巨大
+0

$ ./strings.exe 令牌= 4個 令牌[1] = 129個 令牌[2] = 128個 令牌[3] = 0 令牌[4] = 1個 令牌[5] =(空) – Fred 2012-02-07 17:45:55

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字符串保持不變。所以當你使用這個(或你的原始)函數的時候,你需要牢記這一點 - 當你不需要保存原始字符串或者先複製原始字符串時使用它。