今天在調用strsep函數時,報了Segmentation fault錯誤,strsep函數原型如下:
char *strsep(char **stringp, const char *delim);
第一個參數是個二級指針,而且沒有const修飾,我猜測在man手冊中只要是添加const修飾的參數都是隻讀的(當然這是肯定的),而那些沒有添加const修飾的一般爲可讀可寫的。
根據這種猜測,再根據調用strsep時。傳遞的第一個參數是一個const char *,這時程序運行時就回報Segmentation fault錯誤。
例:
1 #include <stdio.h> 2 3 int main(void) 4 { 5 char *buf = "123456,123456"; 6 7 strsep(&buf, ","); 8 9 return 0; 10 }
編譯運行結果:
# ./a.out Segmentation fault
修改如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int main(void) 6 { 7 char *buf = malloc(32); 8 9 memcpy(buf, "123456,123456", sizeof("123456,123456")); 10 11 strsep(&buf, ","); 12 13 return 0; 14 }
另一種修改方式:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int main(void) 6 { 7 char buf[32] = {0}; 8 9 memcpy(buf, "123456,123456", sizeof("123456,123456")); 10 11 strsep(&buf, ","); 12 13 return 0; 14 }
上邊這種修改在編譯的時候會有一個警告:
4.c: In function ‘main’: 4.c:13:5: warning: passing argument 1 of ‘strsep’ from incompatible pointer type [enabled by default] strsep(&buf, ","); ^ In file included from 4.c:3:0: /usr/include/string.h:555:14: note: expected ‘char ** __restrict__’ but argument is of type ‘char (*)[32]’ extern char *strsep (char **__restrict __stringp, ^
可以將strsep(&buf, ",");修改爲strsep((char **)(&buf), ",");以消除影響。但是這個並不是導致問題的所在。
上邊的修改並沒有解決問題。
再次修改:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int main(void) 6 { 7 char buf[32] = {0}; 8 char *tmp = NULL; 9 10 memcpy(buf, "123456,123456", sizeof("123456,123456")); 11 12 tmp = buf; 13 strsep(&tmp, ","); 14 15 return 0; 16 }
以上兩種方式都可以解決所遇到的問題。
這是什麼原因的導致的呢!以下是從glibc中找到的源碼:
1 char * 2 __strsep (char **stringp, const char *delim) 3 { 4 char *begin; 5 6 assert (delim[0] != '\0'); 7 8 begin = *stringp; 9 if (begin != NULL) 10 { 11 char *end = begin; 12 13 while (*end != '\0' || (end = NULL)) 14 { 15 const char *dp = delim; 16 17 do 18 if (*dp == *end) 19 break; 20 while (*++dp != '\0'); 21 22 if (*dp != '\0') 23 { 24 *end++ = '\0'; 25 break; 26 } 27 28 ++end; 29 } 30 31 *stringp = end; 32 } 33 34 return begin; 35 }
1 char * 2 __strsep (char **stringp, const char *delim) 3 { 4 char *begin, *end; 5 6 begin = *stringp; 7 if (begin == NULL) 8 return NULL; 9 10 /* Find the end of the token. */ 11 end = begin + strcspn (begin, delim); 12 13 if (*end) 14 { 15 /* Terminate the token and set *STRINGP past NUL character. */ 16 *end++ = '\0'; 17 *stringp = end; 18 } 19 else 20 /* No more delimiters; this is the last token. */ 21 *stringp = NULL; 22 23 return begin; 24 }
以上是在glic中找到的倆種實現方法,這兩種方式都對stringp中的數據進行了修改,所以纔會出現上述錯誤。
在解決問題的過程中,還遇見一個問題:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int main(void) 6 { 7 char *buf = malloc(32); 8 9 buf = "123456,123456"; 10 11 memcpy(buf, "123456,123456", sizeof("123456,123456")); 12 13 strsep(&buf, ","); 14 15 return 0; 16 }
上邊這段代碼在運行的時候也會報Segmentation fault錯誤,這其實是因爲buf的指向從堆區換到了只讀存儲區,所以會出現錯誤。這個錯誤很容易發現,但是在編碼的過程中不注意還是很容易犯的,可能是我自身的問題,以作記錄,以謹記。