2014-04-02 22 views
0

下面給出的源代碼是一些更詳細的C源代碼的精簡版本,它解析輸入字符串以查看它們是否匹配預定義的模式。代碼嘗試解析輸入字符串(您可以假設它是一個有效的以空字符結尾的字符串)。如果 該字符串包含有效的無符號整數,則函數返回0,否則返回錯誤-1。無符號整數與正則表達式[0-9] + $匹配。代碼中可能的內存錯誤和可能的解決方案?

我試着運行valgrind命令來找出顯示以下輸出(我無法理解)的可能錯誤。

==15269== 
==15269== Invalid read of size 1 
==15269== at 0x400770: parse_exact (assign2b.c:23) 
==15269== by 0x400957: xtz_parse_unsigned (assign2b.c:82) 
==15269== by 0x400A26: test_parse_unsigned (assign2b.c:102) 
==15269== by 0x400B06: main (assign2b.c:128) 
==15269== Address 0x51f2045 is 0 bytes after a block of size 5 alloc'd 
==15269== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==15269== by 0x4EBAD81: strdup (strdup.c:43) 
==15269== by 0x400AF1: main (assign2b.c:127) 
==15269== 
==15269== Invalid read of size 1 
==15269== at 0x400770: parse_exact (assign2b.c:23) 
==15269== by 0x400957: xtz_parse_unsigned (assign2b.c:82) 
==15269== by 0x400A26: test_parse_unsigned (assign2b.c:102) 
==15269== by 0x400B9B: main (assign2b.c:142) 
==15269== Address 0x51f2135 is 0 bytes after a block of size 5 alloc'd 
==15269== at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==15269== by 0x400B72: main (assign2b.c:140) 

以下是代碼。請告訴代碼和可能的解決方案,以及如何同這些錯誤可以使用的valgrind

#include <stdio.h> 
    #include <ctype.h> 
    #include <assert.h> 
    #include <stdlib.h> 
    #include <string.h> 

    #define OK 9999 
    #define EOS '\0' 
    #define XT_SUCCESS 0 
    #define XT_FAIL -1 

    typedef int (*PARSE_FUNC)(const char *s, const char **endptr); 

    static int parse_exact(const char *s, const char **endptr, PARSE_FUNC pfunc) 
    { 
     const char *cp = s; 
     int c; 
     int state = 1; 
     while (state != XT_SUCCESS && state != XT_FAIL) 
     { 
      c = *cp++; // nextchar 
      switch(state) 
      { 
      case 1: 
       state = pfunc(--cp, endptr); 
       cp = *endptr; 
       if (state == XT_SUCCESS) state = 2; 
       else cp++; // on FAIL jump ahead to get undone on exit 
       break; 
      case 2: 
       if (EOS == c) state = OK; 
       else state = XT_FAIL; 
       break; 
      case OK: 
       state = XT_SUCCESS; 
       break; 
      default: 
       /* LOGIC ERROR */ 
       assert(0==1); 
       break; 
      } 
     } 
     if (endptr) 
      *endptr = --cp; 
     return state; 
    } 

    static int base_unsigned(const char *s, const char **endptr) 
    { 
     const char *cp = s; 
     int c; 
     int state = 1; 
     while (state != XT_SUCCESS && state != XT_FAIL) 
     { 
      c = *cp++; // getnextchar 
      switch(state) 
      { 
      case 1: 
       if (isdigit(c)) state = 2; 
       else state = XT_FAIL; 
       break; 
      case 2: 
       if (isdigit(c)) state = 2; 
       else state = XT_SUCCESS; 
       break; 
      default: 
       /* LOGIC ERROR */ 
       assert(0==1); 
       break; 
      } 
     } 
     if (endptr) 
      *endptr = --cp; 
     return state; 
    } 

    int xtz_parse_unsigned(const char *s, const char **endptr) 
    { 
     PARSE_FUNC pfunc = base_unsigned; 
     return parse_exact(s, endptr, pfunc); 
    } 

    void xt_pr_error(int status, const char *s, const char *endptr) 
    { 
     if (0 != status) 
     { 
      if (endptr[0]) 
       printf("ERROR: '%c' at position %d is not allowed", *endptr, (endptr - s)+1); 
      else if ((endptr - s) > 0) 
       printf("ERROR: cannot end with '%c'", endptr[-1]); 
      else 
       printf("ERROR: value is empty"); 
     } 
    } 

    void test_parse_unsigned(const char *s, int expected) 
    { 
     int status; 
     const char *endptr; // Ptr to first invalid character 
     status = xtz_parse_unsigned(s, &endptr); 
     printf("Test input='%s' status=%d ", s, status); 
     xt_pr_error(status, s, endptr); 
     if (status != expected) 
      printf(" NOT EXPECTED!\n"); 
     else 
      printf(" (OK)\n"); 
    } 


    int main(void) 
    { 
     char s1234[] = "1234"; 
     char s12a4[] = "12a4"; 
     char *ptr; 

     // Tests with string literals 
     test_parse_unsigned("1234", XT_SUCCESS); 
     test_parse_unsigned("12a4", XT_FAIL); 

     // Tests with static strings arrays 
     test_parse_unsigned(s1234, XT_SUCCESS); 
     test_parse_unsigned(s12a4, XT_FAIL); 

     // Tests using strdup() 
     ptr = strdup("1234"); 
     test_parse_unsigned(ptr, XT_SUCCESS); 
     free(ptr); 

     ptr = strdup("123a"); 
     test_parse_unsigned(ptr, XT_FAIL); 
     free(ptr); 

     ptr = strdup("1a34"); 
     test_parse_unsigned(ptr, XT_FAIL); 
     free(ptr); 

     // Test using malloc and strcpy() 
     ptr = malloc(5); 
     strcpy(ptr, "1234"); 
     test_parse_unsigned(ptr, XT_SUCCESS); 
     free(ptr); 

     return 0; 
    } 
+0

請指明'assign2b.c:23'是。 – timrau

回答

1

這是很難從你的代碼中真正的錯誤是告訴推斷,你必須通過它與一個調試器。但從它的外觀來看,超出1個字節的讀取,您的字符串沒有正確的空終止,或者您沒有很好地處理這種情況。

valgrind指向的函數有點難以理解,因爲對於字符串結尾沒有明確的條件,也就是c'\0'

另外:

之類的東西*cp++屬於改建爲博物館,不使用表達式的副作用。在這裏,而不是while循環的,你可以輕鬆擁有for環路與cp作爲迭代變量

for (const char *cp = s; 
    state != XT_SUCCESS && state != XT_FAIL; 
    ++cp) { 
    ... 
} 

你使用你的狀態變量具有命名常量和數字的混合是瘋狂和不可讀爲他人或自己,如果你回來一週後

0

在函數parse_exact中,您正在讀取超出EOS的一個位置。具體而言,這是當你到達輸入字符串的結尾會發生什麼:

c = *cp++; // nextchar 

讀取NULL字符(EOS)。

if (EOS == c) state = OK; 

由於狀態既不是XT_SUCCESS也不XT_FAIL,另一路經地穿過該環製成。

c = *cp++; // nextchar 

讀取超出EOS的字符。在一些系統上,這是可以接受的,但是通過嚴格的邊界檢查,事實並非如此。在你的情況下,發生錯誤。

case OK: 
    state = XT_SUCCESS; 

所以最終,state會成爲XT_SUCCESS。讓我想知道爲什麼你有這個中間狀態OK

我建議你放棄OK,並在XT_SUCCESS這行代碼替換:

if (EOS == c) state = OK; 
相關問題