關於strsep函數以及聯想

今天在調用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的指向從堆區換到了只讀存儲區,所以會出現錯誤。這個錯誤很容易發現,但是在編碼的過程中不注意還是很容易犯的,可能是我自身的問題,以作記錄,以謹記。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章