2014-04-02 1 views
0

아래 제공된 소스 코드는 입력 문자열을 파싱하여 미리 결정된 패턴과 일치하는지 확인하는 좀 더 자세한 C 소스 코드의 컷 다운 버전입니다. 이 코드는 입력 문자열 (유효한 null로 끝나는 문자열이라고 가정 할 수 있음)을 구문 분석하려고 시도합니다. 문자열에 유효한 부호없는 정수가 들어 있으면이 함수는 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

가 표시하시기 바랍니다. – timrau

답변

1

를 사용하여 추론 할 수있는 코드와 가능한 해결 방법과 같은에서 오류를 말해, 당신은 그것을 통해 가야 할 것 디버거. 그러나 그것의 모양에서 1 바이트 너비 이상으로 읽으면 문자열이 제대로 null로 종료되지 않거나 그 조건을 잘 처리하지 못합니다.

문자열 끝의 명시적인 조건이 없기 때문에 valgrind가 가리키는 함수는 이해하기가 약간 어렵습니다. c'\0' 인 경우입니다. 또한

는 : *cp++ 같은

상황이 부작용에 대한 표현을 사용하지 않는, 박물관에 속한다. 여기에 대신 while 루프의 당신은 쉽게 변수

for (const char *cp = s; 
    state != XT_SUCCESS && state != XT_FAIL; 
    ++cp) { 
    ... 
} 
라는 상수와 숫자로 상태 변수를 사용하여 귀하의 혼합물 미친 다른 사람 또는 자신에 대한 읽을

당신이 돌아올 경우 cp 반복으로와 for 루프를 가질 수있다 1 주일 후

0

기능이 parse_exact 인 경우 EOS 이후 한 위치를 읽는 중입니다.

c = *cp++; // nextchar 

는 NUL 문자 (EOS)를 읽 구체적으로, 이것은 당신이 입력 문자열의 끝에 도달 할 때 발생하는 것입니다. 상태도 아니고 XT_SUCCESSXT_FAIL이다

if (EOS == c) state = OK; 

때문에, 또 다른 패스는 루프를 통해 이루어진다.

c = *cp++; // nextchar 

EOS 이상의 문자를 읽습니다. 일부 시스템에서는 이것이 허용되지만 엄격한 범위 검사를 통해 허용되지는 않습니다. 귀하의 경우 오류가 발생합니다.

case OK: 
    state = XT_SUCCESS; 

그래서 결국 stateXT_SUCCESS이 될 것입니다. 이 중간 상태가 OK 인 이유가 궁금합니다.

난 당신이 OK을 포기하고,이 코드 줄에 XT_SUCCESS에 의해 대체 제안 : 23`이다 :`assign2b.c 어디

if (EOS == c) state = OK; 
관련 문제