malloc() 후에 free()가 안됩니다. 세그먼테이션 폴트
안녕하세요.
이틀을 고민하고 검색을 했으나 원하는 답을 얻지 못해 힘들게 질문드립니다.
도움 부탁드립니다. 정말 밥이 목구멍으로 넘어가지 않습니다 ㅠㅠ
이중 포인터라 지저분하지만 한번 봐주시겠습니까?
1. 제가 메인에서 포인터를 선언하고,
2. 첫번째 함수에서 파라미터로 넘겨 malloc을 사용하여 메모리를 할당합니다.
3. 그리고 두번째 함수에서 또 포인터를 파리미터로 받아서 free를 하게 됩니다.
그런데 문제는 free가 되지 않고 정확하게 이곳에서 세그먼테이션 폴트가
뜨는군요. gdb에 의하면 mallopt에서 문제가 발생했다는 메시지를 남깁니다.
일단 문제의 free만 주석 처리를 하고 프로그램을 컴파일 하면 문제가 없고
동작도 잘 합니다. 하지만 저 위치 아니면 free를 해줄 수 가 없을 것
같아서 걱정입니다.
소스코드 전부를 올리면 보기 힘드실 것 같아서 메인과 malloc와 free하는
부분만 올리겠습니다. 원본 파일은 첨부하겠습니다.
k&r 2판의 예제 1-20을 풀다가 문제와는 상관없이 생기는 오류때문에
난관에 봉착했습니다. ㅠㅠ (문제 자체는 간단합니다...탭을 스페이스로 치환)
지저분한 소스지만 부탁드립니다. 결정적으로 왜 저 위치에서 free(*str)을
했을경우 세그폴트가 뜨는지 정말 알고 싶습니다.
우선 메인입니다. 문제가 발생하는 free(*str)은 dtab()함수에 존재합니다.
int main(int argc, char *argv[]) { char *result; if ( !check_arg(argc, argv) ) exit(EXIT_FAILURE); if ( !read_file(argv[1], &result) ) exit(EXIT_FAILURE); if ( !dtab(&result) ) exit(EXIT_FAILURE); if ( !write_file(argv[1], result) ) exit(EXIT_FAILURE); free(result); return 0; }
메모리 할당해주는 부분입니다. (여기는 문제가 없네요.)
/***************************************************************** * * Function Name: read_file * * Input : const char *filename, char **str * Output : int 1(success), 0(failure); * * Comment : 텍스트 파일의 내용을 문자열 배열에 덤프한다. * 문자열 배열 포인터를 받아와서 그 값을 기록한다. * 문자열 배열은 이 함수 내에서 메모리 할당이되며 * 이 문자열 배열을 사용하고 난 뒤에는 해제되어야만 * 한다. 파라미터로 오는 변수는 값이 할당되지 않은 * 순수 포인터여야한다.(배열은 안됨!) * *****************************************************************/ int read_file(const char *filename, char **str) { int n_cnt; char buf[MAXLINE]; FILE *fp; if ( (fp = fopen(filename, "r")) == NULL ) { fprintf(stderr, "[read_file: %d] Can't open file: %s\n", __LINE__, filename); return 0; } /* 파일 내용 복사를 위한 여유 공간 마련 */ n_cnt = 0; while ( fgets(buf, MAXLINE, fp) != NULL ) n_cnt += strlen(buf); *str = (char *)malloc(sizeof(char) * (n_cnt + 1)); if ( *str == NULL ) { fprintf(stderr, "[read_file: %d] Mem alloc err\n", __LINE__); return 0; } /* 파일 포인터 초기 위치로 */ if ( fseek(fp, 0, SEEK_SET) != 0 ) { fprintf(stderr, "[read_file: %d] file pointer seek err\n", __LINE__); return 0; } /* dump!! */ while ( fgets(buf, MAXLINE, fp) != NULL ) strcat(*str, buf); return 1; }
이곳이 바로 문제가 생기는 부분입니다!
정확하게 free(*str)에서 문제가 생깁니다.
/***************************************************************** * * Function Name: dtab * * Input : char **str * Output : int 1(success), 0(failure) * * Comment : file의 내용을 모두 덤프시킨 문자배열 포인터를 * 받아와서(str) 탭문자를 스페이스로 변환시킨다. * 단 탭의 위치에 해당하는 만큼의 스페이스 개수로 * 치환한다. * (*str)[i] 표현에 주의할것, * str[i] 는 원하지 않는 결과 출력시킴 * *****************************************************************/ int dtab(char **str) { int i, j, k, l; int n_tab, n_sp; char *new_str; /* tab 개수 체크 */ n_tab = 0; for ( i = 0; *str[i] != '\0'; ++i ) if ( *str[i] == '\t' ) ++n_tab; /* 파라미터 복사본 - 약간의 메모리 낭비 허용 */ new_str = (char *)malloc(sizeof(char) * (strlen(*str) + (n_tab - 1) * TABSIZE + 1)); if ( new_str == NULL ) { fprintf(stderr, "[dtab: %d] malloc failure\n", __LINE__); return 0; } /* tab -> space!! */ for ( i = 0, j = 0; (*str)[i] != '\0'; ++i ) { new_str[j] = (*str)[i]; if ( new_str[j] == '\n' ) { k = 0; ++j; } else if ( new_str[j] == '\t' ) { n_sp = TABSIZE - (k % TABSIZE); for ( l = 0; l < n_sp; ++l ) new_str[j++] = ' '; } else { ++k; ++j; } } new_str[j] = '\0'; /* 원본 해제 후 복사본을 연결 */ /* 문제가 있는 부분 */ free(*str); /* 이부분이 문제입니다. */ *str = new_str; return 1; }
데비안 리눅스 2.4.28gcc 3.3.5환경에서 수행했습니다
데비안 리눅스 2.4.28
gcc 3.3.5
환경에서 수행했습니다.
----
use perl;
Keedi Kim
[quote]* (*str)[i] 표현에 주의할것, *
*str[i] 와 (*str)[i]는 의미가 다르겠네요.
이 부분을 수정해야겠습니다.
귀차니즘이 발생해서..
일단 malloc후의 *str에 부여된 메모리 주소랑 free하기 직전의 *str에 부여된 주소랑 비교해서 malloc후의 부여된 메모리 주소와 틀릴때에는 분명 malloc후free하기 전까지 어디선가 값이 틀리게 바뀌어 버렸을 가능성이 크구요..
-g 옵션 주시고 컴파일 하신후에 gdb 로 디버깅 하시면 훨씬 문제점을 발견하시기 쉽습니다.
우선은 read_file에서부터 문제가...
우선 read_file() 에서부터 문제가 있군요.
read_file() 끝부분에
strcat 함수는 문자열의 끝에 추가해줍니다. 즉 대상버퍼에서 0이란 한 바이트를 찾아 그 위치부터 추가하죠.
그런데 *str 이 가리키는 버퍼엔 끝이 어딘지 모르므로 아마 엉뚱한 위치에 문자열들이 추가되었을 가능성이 있네요. 그게 아마 동적할당 메모리 블럭 구조를 망가뜨린게 원인이 아닐까 합니다.
일단 나머진 좀 더 봐야겠네요.
님ㅎ 즐~
Re: 우선은 read_file에서부터 문제가...
아닙니다. 처음부터 추가합니다.
----
Let's shut up and code.
read_file()함수와 dtab()함수에서의 *str주소값이 같은지를
read_file()함수와 dtab()함수에서의 *str주소값이 같은지를 먼저 확인을 해봐야 할것 같군요.
제가 볼땐 별다른 조작이 없어서 변조 된 것은 없을거 같은데...?
가끔 프로그래밍 하다 보면은 이해가 안되는 일도 있기는 하죠.
<어떠한 역경에도 굴하지 않는 '하양 지훈'>
#include <com.h> <C2H5OH.h> <woman.h>
do { if (com) hacking(); if (money) drinking(); if (women) loving(); } while (1);
문제는 free()루틴 이전의 malloc()루틴입니다.dtab()
문제는 free()루틴 이전의 malloc()루틴입니다.
dtab() 함수에서
이 부분은, 원하시는 결과를 위해서는 다음과 같이 되어야겠죠.
저 오류 때문에, n_tab의 개수에 따라서 malloc이 실패할 가능성이
있습니다. (간단히 생각해서 n_tab = 0일 경우, 음수가 될 수도 있죠.)
여기를 수정해주시면 문제 없이 작동할 것 같습니다.
----
Let's shut up and code.
조금 긴 파일을 돌려봤더니 에러가 나네요.dtab() 함수 안의/
조금 긴 파일을 돌려봤더니 에러가 나네요.
dtab() 함수 안의
/* tab -> space!! */
부분을 어떻게 생각하시고 코딩하신건지 설명 부탁드리겠습니다.
k도 초기화되지 않았군요.
----
Let's shut up and code.
strcat 함수는 끝에 추가합니다.
strcat 함수는 목적지 주소로부터 0을 찾은 다음 그 위치에서부터 추가합니다.
처음이 아닙니다.
님ㅎ 즐~
연습문제 때문에 밥이 목구멍에 넘어가지 않으신다니 무척 열심이시군요. ^
연습문제 때문에 밥이 목구멍에 넘어가지 않으신다니 무척 열심이시군요. ^^
그래서 그런지 많은 분들이 도와주시나 봅니다. 저도 한마디 거들겠습니다.
첫라인이 '\n'으로 시작하지 않으면 k가 초기화되지 않은 상태로 되니까요.
1장 연습문제를 하시면서 malloc에 저수준 파일 입출력까지... ^^;
이미 상당히 알고 계시는 분이군요. 그런데 마지막장을 제외한 TCPL의 모든 연습문제는
표준 라이브러리만 갖고도 풀 수 있는 것들입니다. 처음에는 그렇게 시도하신 것
같은데 실패하셨나 보군요. write_file()의 fp는 int가 아니라 FILE*이어야 합니다.
하드를 뒤져보니 예전에 제가 풀었던 코드가 있군요. 참고하시라고 올려봅니다.
(생각해 보니 공부하면서 풀었던 연습문제 코드를 코드 놀이터 게시판에 올리는
것도 재미있겠는데요.) 지금 같으면 getline() 대신 fgets()를 썼겠지만 책에 나온
코드를 그대로 쓰느라고 그랬던 것 같습니다.
Re: strcat 함수는 끝에 추가합니다.
헉! 죄송합니다. 제가 잘못 알았군요.. :-) 생각해보니 처음부터라면
마지막 줄만 기록이 되겠군요. :oops: 정정해 주셔서 감사합니다.
그건 그렇고, 문제점 하나를 또 찾았습니다. 이게 치명적이었던 거 같습니다. :twisted: dtab()에서
주석에도 나와 있는 거 같았는데, (*str)[i] 가 되어야겠죠.
이 코드를 보면서 다시한번 느낀 점이지만, 포인터가 정말 파워풀하긴
하지만, 과도한 사용은 좋지 않다는 겁니다. :-)
----
Let's shut up and code.
아... 살려주셔서 감사합니다. 정말 속이 다 시원하군요 ㅠㅠ당장
아... 살려주셔서 감사합니다. 정말 속이 다 시원하군요 ㅠㅠ
당장(free시 세그먼테이셜 폴트)의 가장 큰 오류는 (*str)[i] 였습니다.
그리고 지적해주신 너무나도 중요한 잠재적인 오류들 너무 감사히
참고했습니다.
전 정말 간단한 문제조차도 이렇게 버그가 많을줄 몰랐습니다.
나름대로 초기화도 다 해야지, 포인터 처리도 다 해야지,
메모리할당도 정확하게 해야지... 하면서도 저렇게 많은
부분들이 틀릴줄 몰랐습니다. 어설프게 아는 것이 무섭다는 것도
다시 한번 느끼게 되었습니다.
정말 오늘 다시한번 코딩 습관 스타일, 그리고 특히 앞으로
포인터를 사용할때 주의하고 그리고 또! 또! 한번 더 주의해야
할 점들을 너무나도 많이 배워갑니다.
감사합니다.
yoocj9님 감사합니다.
(*str)[i] 에 대한 지적 감사합니다.
가장 핵심적인 오류 였습니다.
손님님, 서지훈님 감사합니다.
메모리 위치 테스트를 해보았는데 신기하게도
세그폴트가 날때도 메모리 주소는 메인과 read_file()과
dtab()에서 동일했습니다. 아마도 저의 치명적 실수(*str)[i]
가 무언가를 건드렸던 것 같습니다.
hanzo69님 감사합니다.
strcat()시 초기화 문제는 당장은 알 수 없었지만 앞으로
겪게 될 무수한 버그들로 부터 절 살려주셨습니다.
제 C언어에 대한 무지함을 다시 한번 알려 주셨습니다.
sangwoo님 감사합니다.
dtab()내의 malloc()에서 n_tab개수 오류 문제,
k의 초기화 문제, (*str)[i] 문제 체크는 정말
역시 앞으로 겪게 될 무수한 버그로 부터 살려주시고
다시 한번 제 코딩 습관에 대해 생각해보게 되었습니다.
doldori님 감사합니다.
k의 초기화 문제, 파일포인터의 타입 문제 체크
그리고 참고 소스 감사하게 잘 받았습니다.
제 코드에 쓸데 없는 코드만 너무 많았다는 것을 또
깨달았습니다. ㅠㅠ
----
use perl;
Keedi Kim
댓글 달기