[英]Function to split string sometimes gives 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;
}
split[count - 1][strlen(split[count - 1]) - 1] = '\0';
應該看起來像
split[count - 1] = NULL;
您沒有在此分配任何內容,因此可以對其進行訪問並放入“ \\ 0”。
之后,將該行放在while(split[i])
之前while(split[i])
以便while到達NULL時可以停止。
函數strtok不是可重入的,請使用strtok_r()函數,這是可重入的版本strtok()。
您有許多細微的問題,如果傳遞字符串文字,您的函數就會出現段錯誤。 您需要復制要拆分的字符串,因為strtok
會修改該字符串。 如果傳遞字符串文字(存儲在只讀存儲器中),則編譯器將無法發出警告,除非您將string
聲明為const char *string;
為避免這些問題,只需復制將標記的字符串的副本即可。 這樣,無論如何聲明傳遞給函數的字符串,都可以完全避免問題。
您還應該將指向size_t
的指針作為參數傳遞給函數,以使令牌數在調用函數中可用。 這樣,您不必在指向返回的char指針的指針中保留定點NULL作為最終指針。 只需傳遞一個指針並對其進行更新以反映您的函數中解析的令牌數。
將這些碎片放在一起,並進行一些清理,可以使用以下方法來完成您要嘗試做的事情:
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)
始終確認已釋放已分配的所有內存,並且沒有內存錯誤。
仔細檢查一下,如果您還有其他問題,請告訴我。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.