2017-02-15 1 views
1

Hei, 나는이 학교 운동을 해결하려고 노력했다.Realloc으로 인한 세그먼트 오류?

문자열을 읽고 계속 읽는 프로그램을 작성하여 단일 문자열에 추가한다. 연결은 성공하면 1을 반환하고 실패하면 0을 반환하는 함수에서 수행되어야합니다. 메모리 할당을 위해서만 realloc을 사용하십시오!

프로그램을 디버깅하는 동안 오류가 발생하지 않지만 문자열을 삽입 한 후 프로그램을 실행하려고하면 "세그멘테이션 오류"가 표시 될 수 있습니다.

1) 당신은 힙에 자신을 할당 free해야 것만 : 코드 적어도 5 문제가 있습니다

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

int cat(char **, char *); 

int main(void) 
{ 
    char string[51]; 
    char *output=NULL; 
    char choice; 
    do 
    { 
    printf("Please enter a string [<50 chars]: "); 
    fgets(string,50,stdin); 
    if(string[strlen(string)-1]=='\n') /* if newline was read as well */ 
     string[strlen(string)-1]=0;  /* discard it */ 
    if(cat(&output,string)) 
     printf("\n\nThe string now contains:\n%s\n",output); 
    else 
    { 
     printf("error: memory (re-)allocation failed!\n\n"); 
     return 1; /* exit with error */ 
    } 
    printf("Continue? (y/n) - "); 
    fgets(string,3,stdin); /* read input from keyboard - leave a safety buffer to account for read newline */ 
    choice=string[0]; /* use the first character from the previous read as the choice */ 
    } while(choice=='y' || choice=='Y'); 

    free(output); 
    return 0; 
} 

int cat(char **dest, char *src) 
{ 

    int i; 
    int length1=strlen(src); 
    int length2=strlen(*dest); 
    int length3=length1+length2; 
    *dest=(char*)realloc(NULL,sizeof(*src)); 
    printf("%p", *dest); 
    if(*dest==NULL) return 0; /* if allocation failed */ 
    for(i=0;i<=length3;i++) 
    { 
     if(i<=length1) 
     (*dest)[i]=(*dest)[i]; 
     else 
     (*dest)[i]=(src)[i]; 
    } 
    free(src); 
    return 1; 
} 
+0

에 오신 것을 환영처럼 보일 수 있습니다. [The Tour] (http://stackoverflow.com/tour)를 읽으신 후 [Help Center] (http://stackoverflow.com/help/asking)의 자료를 참조하십시오. 여기에 물어보십시오. –

+0

이러한 문제를 해결하는 올바른 도구는 디버거입니다.스택 오버플로를 묻기 전에 코드를 단계별로 실행해야합니다. 자세한 도움말은 [작은 프로그램 디버깅 방법 (Eric Lippert 작성)] (https://ericlippert.com/2014/03/05/how-to-debug-small-programs/)을 참조하십시오. 문제를 재현하는 [최소, 완료 및 확인 가능] (http://stackoverflow.com/help/mcve) 예제와 함께 해당 질문을 \ [편집]해야합니다. 디버거. –

+0

'toupper (choice) == 'Y')'와 같이 toupper() 또는 tolower()를 사용하여'choice'를 비교할 수 있습니다. –

답변

4

:이 코드입니다. src에서 전달하는 내용이 스택 메모리 (자동으로 char string[51];이 해제 됨)를 가리 키기 때문에 free(src)을 수행하지 마십시오.

2) 아마도 dest을 다시 할당하려했기 때문에 3) length3 (+1 널 종단기 +1) 크기의 메모리를 할당하려고했습니다. *dest 처음에 NULL 인 경우

*dest=(char*)realloc(*dest, length3 + 1); 

4) strlen(*dest) 가 충돌합니다.

int length2=(*dest)?strlen(*dest):0; 

5) for-loop가 올바르지 않다고 생각합니다. 문자열을 연결하지 않으므로 오프셋 계산이 해제됩니다.

+0

나는 당신이 말한대로했다. 그러나 여전히 나에게 똑같은 에러를 주었다. –

+0

realloc 된 메모리에 대한 포인터에'realloc()'의 반환 값을 직접 할당함으로써, 데이터에 대한 포인터를 잃을 위험이있다. 메모리 누출. 'realloc()'이 실패하면 널 포인터가 반환됩니다. 반환 값을 임시 포인터에 저장 한 다음 할당이 성공했는지 확인해야합니다. –

1

포인터 output의 초기 값은 NULL입니다. 그러나 함수 내부에서 포인터가 NULL과 같은지 확인하지는 않습니다. 따라서 함수 strlen을 포인터에 적용하면 정의되지 않은 동작이 발생합니다.

또한 종료 영점 문자를 하나 더 예약해야합니다.

메모리가 함수에서 올바르게 재 할당되지 않았습니다. 또한 sizeof(*src)은 1 바이트와 같습니다.

이 문

if(i<=length1) 
    (*dest)[i]=(*dest)[i]; 

는 큰 의미가 없습니다. 재 할당 된 메모리에 올바르게 재 할당 된 경우 이미 원래 문자열이 들어 있습니다.

포인터가 동적으로 할당 된 메모리를 가리 키지 않기 때문에 src 포인터를 해제하면 안됩니다.

기능은 데모 프로그램에서 보여 지듯이 다음과 같은 방식으로 보일 수 있습니다.

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

int cat(char **dest, const char *src) 
{ 
    size_t n = strlen(src) + (*dest == NULL ? 0 : strlen(*dest)); 

    char *tmp = realloc(*dest, n + 1); 
    int success = tmp != NULL; 

    if (success) 
    { 
     if (*dest == NULL) *tmp = '\0'; 
     *dest = tmp; 

     while (*tmp) ++tmp; 

     while ((*tmp++ = *src++)); 
    }  

    return success; 
} 

#define N 50 

int main(void) 
{ 
    char *output = NULL; 
    char choice = 'n'; 

    do 
    { 
     char string[N]; 


     printf("Please enter a string [<%d chars]: ", N); 
     fgets(string, sizeof(string),stdin); 

     string[strcspn(string, "\n")] = '\0'; 

     if (cat(&output, string)) 
     { 
      printf("\nThe string now contains:\n\"%s\"\n\n", output); 
     }   
     else 
     { 
      printf("error: memory (re-)allocation failed!\n\n"); 
      return 1; /* exit with error */ 
     } 

     printf("Continue? (y/n) - "); 
     fgets(string, 3, stdin); /* read input from keyboard - leave a safety buffer to account for read newline */ 
     choice = string[0]; /* use the first character from the previous read as the choice */ 
    } while (choice == 'y' || choice == 'Y'); 

    free(output); 

    return 0; 
} 

그것의 출력은 스택 오버플로

Please enter a string [<50 chars]: Hi Stefano Feltre 

The string now contains: 
"Hi Stefano Feltre" 

Continue? (y/n) - y 
Please enter a string [<50 chars]: 

The string now contains: 
"Hi Stefano Feltre " 

Continue? (y/n) - y 
Please enter a string [<50 chars]: Let's learn C 

The string now contains: 
"Hi Stefano Feltre Let's learn C" 

Continue? (y/n) - n 
+0

@Stefano Feltre 답안에서 시범 프로그램을보십시오. –

+0

음 ... 매크로를 사용하지 않고 만들 수 없습니까? 또한이 연습을 위해 함수를 다음과 같이 유지해야합니다. int cat (char ** dest, char * src) 수정할 수 없습니다. 그리고 왜 정말 * tmp 변수가 필요한지 이해할 수 없습니다 ... realloc에 ​​변수 * dest를 직접 사용할 수 있습니까? Thx –

+0

@StefanoFeltre 매크로는 마술 번호 50 대신 이름을 도입합니다. 마술 번호 대신 명명 된 상수를 사용하는 것이 항상 좋습니다. 컴파일러가 가변 길이 배열을 지원하면 변수를 상수 변수로 대체 할 수 있습니다. 예를 들어 const size_t N = 50; 한정자 const를 사용하여 두 번째 매개 변수를 선언해야합니다. 함수 내에서 매개 변수가 변경되지 않는 함수를 사용하는 함수와 클라이언트 사이의 계약입니다. realloc이 NULL을 리턴 할 수 있기 때문에 변수 tmp가 필요합니다. 이 경우 dest의 값이 손실됩니다. –

관련 문제