2016-06-11 2 views
-1

나는 K & R의 연습 문제를 연구 중이며 04-06을 다음과 같이 확장하는 동안 이상한 버그가 발생했습니다. 문자열 이름이있는 변수를 허용하십시오. 솔직히, 나는 실제로 버그 (꽤 간단하게 - 아래에 설명 됨)를 고치지 만, 처음에 왜 에러가 발생했는지 알고 싶습니다.snprintf에 비어 있지 않은 문자열을 전달하면 관련이없는 char * 배열로 인해 주소가 변경됩니다.

이 문제에 익숙하지 않은 사용자는 기본적으로 문자 이름을 사용하여 변수를 저장하고 호출 할 수있는 명령 줄 계산기 (폴란드어 표기법 사용)를 작성해야합니다. 문제가 발생하는 위치

다음은 관련 코드입니다 :

#define MAXOPLEN 1000 

int varCount = 1; 
char **keys; 
char **values; 
// changing the declaration to: 
// char strOps[][STROPCOUNT] = { ... }; 
// fixed the issue 
char *strOps[STROPCOUNT] = { "dupe", "swap", "del", "print", 
          "clr", "sin", "cos", "tan", 
          "exp", "pow", "ln", "log", 
          "mem", "re"}; 

main() { 
    keys = malloc(varCount * sizeof(char[MAXOPLEN])); 
    keys[0] = "ans"; 
    values = malloc(varCount * sizeof(char[MAXOPLEN])); 
    values[0] = "0.0"; 

    ... // Other stuff related to the program 
} 

// flag is unrelated to the problem I'm asking about. It just checks to see 
// if the variable name used to store value n is 'ans', which is where 
// the last returned value is stored automatically 
void memorize(char s[], double n, bool flag) { 
    ... // small conditional block for flag 

    for (i = 0; i < varCount; i++) { 
     if (equals(keys[i], s)) { 
      found = True; 
      // Next line is where the program actually breaks 
      snprintf(values[i], MAXOPLEN, "%f", n); 
      break; 
     } 
    } 

    if (!found) { 
     i = varCount; 
     varCount++; 

     keys = realloc(keys, varCount * sizeof(char*)); 
     keys[i] = malloc(sizeof(char[MAXOPLEN])); 
     keys[i] = s; 

     values = realloc(values, varCount * sizeof(char*)); 
     values[i] = malloc(sizeof(char[MAXOPLEN])); 
     snprintf(values[i], MAXOPLEN, "%f", n); 
    } 
} 

컴파일하고 실행 한 후, 당신이 계산하는 방정식에 입력 한 처음, 모든 것이 원활하게 실행 것으로 보인다. 그러나, 디버깅하는 동안 strOps의 처음 세 char *가 이상하게 다른 주소를 가리 키도록 만들어 졌다는 것을 알게되었습니다. 방정식의 반환 값을 "ans"로 저장하려고하면 문자열 s가 이미 키 이름으로 사용되었는지 확인하려고하는 memorize()의 for-loop를 입력합니다. s의 값 ("ans")과 일치하는 문자열을 가리키는 키 [0]을 올바르게 찾은 다음 double n을 문자열로 변환하고 값 [0]에 저장하려고 시도합니다.

현재 snprintf() 함수 내부 strOps의 처음 세 숯 *는 corecrt_stdio_config.h이 방법 안에 다른 가리키게되지만 :

_Check_return_ _Ret_notnull_ 
__declspec(noinline) __inline unsigned __int64* __CRTDECL __local_stdio_printf_options(void) 
{ 
    // Error occurs after this next line: 
    static unsigned __int64 _OptionsStorage; 
    return &_OptionsStorage; 
} 
상기 코드에 주석 같이

제작 strOps 차원 문자 배열 (char 포인터 배열이 아닌)이 문제를 해결했습니다. 문자 배열은 개별 문자의 값을 변경할 수 없기 때문에 이해할 수 있습니다. 그러나 이해할 수없는 것은 corecrt_stdio_config.h의 해당 메서드가 처음 세 포인터의 값을 변경하는 이유입니다.

감사합니다.

+0

정확한 프로토 타입 함수 선언자를 사용하여 시작하십시오. 'main()'은 더 이상 사용되지 않습니다. 그리고 더 최근의 책을 사용하십시오, K & R은 오래되었습니다. 그것은 현대 C를 가르치지 않는다! 그리고 [ask]를보고 [mcve]를 제공하십시오. 경고없이 컴파일 된 코드라도 이해가되지 않습니다. – Olaf

+0

추가 실행과 관련이있는 경우 함수의 결과를 확인하십시오! 'realloc'이 실패 할 수도 있습니다! – Olaf

답변

2

귀하의 초기화가 잘못하고 변화를 일으키는 :

keys[0] = "ans"; 
values[0] = "0.0"; 

모두 "ans""0.0"문자열 리터럴하고 배열을 초기화 할 수 없습니다, 당신은 당신이 할당 후 strcpy를 사용해야합니다.

strcpy (keys, "ans"); 
strcpy (values, "0.0"); 

귀하의 다른 옵션은 한 번에 하나 개의 문자를 할당하는 것입니다 :

size_t i; 
char *p = "ans"; 
for (i = 0; i < strlen (p); i++) 
    keys[i] = p[i];     /* copy to keys */ 
p[i] = 0;       /* nul-terminate */ 

참고 :이이 오류의 예입니다, 당신은 당신의 코드를 통해 같은 일을한다.

+0

시도 할 때 일어나는 일에 대해 조금 설명해 주시겠습니까? keys [0] = "ans"; ? – velocirabbit

+0

'strOps'는 문자열 리터럴 목록으로 초기화 된 포인터 배열입니다. 'keys'는 char *에 대한 포인터 포인터입니다.'keys'를 할당 할 때'varCount * sizeof (char [MAXOPLEN])'('~ 125 개의 초기화되지 않은 포인터 '에 충분한'1000' 바이트) 블록을 만듭니다. 'keys [0] = 'ans';'를 지정하면''ans ''(읽기 전용 메모리)의 주소를'keys'의 첫 번째 포인터에 할당합니다. 그런 다음'keys'를 * char * 포인터로 접근하려고하면'keys'에 대한 주소가 변경되었습니다 (배열의 * 주소는 첫 번째 요소의 주소 *이므로). –

+0

그건 의미가 있습니다. 나중에 키 [0]의 값을 변경하면 strOps의 처음 세 포인터가 변경됩니까? 내가 말할 수있는 한, 그들은 어떤 식 으로든 관련이 없습니다. – velocirabbit