c언어 메모리 해제 런타임 오류
글쓴이: whdtjr222 / 작성시간: 일, 2022/06/19 - 7:42오후
#include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 1024 typedef struct { char* movie_title; double movie_rate; }movie_t; int get_movies_num(FILE* fp); void get_movie_data(movie_t* data, FILE* fp1, FILE* fp2, int movie_data_num); int main(void) { int n; // 영화의 개수 변수 movie_t* data; FILE* fp1; FILE* fp2; fp1 = fopen("movies.txt", "r"); if (fp1 == NULL) { printf("error"); exit(1); } fp2 = fopen("output.txt", "w"); if (fp2 == NULL) { printf("error"); exit(1); } n = get_movies_num(fp1); // 영화 구조체 개수얻기 fseek(fp1, 0, SEEK_SET); // fp1 파일의 위치포인터 초기화 data = (movie_t*)malloc(sizeof(movie_t) * n); // 영화 구조체를 읽어 온 개수 만큼 동적할당 get_movie_data(data, fp1, fp2, n); fclose(fp1); fclose(fp2); free(data); } int get_movies_num(FILE* fp) { char c = 0; int count = 0; while (c != EOF) { c = fgetc(fp); if (c == EOF) { break; } if (c == '\n') { ++count; } } return count/2; } void get_movie_data(movie_t* data, FILE* fp1, FILE* fp2, int movie_data_num) { char buffer[SIZE]; for (int i = 0; i < movie_data_num; i++) { // 파일fp1에서 데이터 읽어서 구조체에 저장 fgets(buffer, SIZE, fp1); buffer[strlen(buffer) - 1] = '\0'; // fgets 함수 \n을 붙여주는걸 \0으로 바꿔줌 data[i].movie_title = (char*)malloc(sizeof(char) * (strlen(buffer)-1)); // 구조체에서 영화제목 저장변수 길이를 영화제목 길이에 맞게 동적할당 strcpy(data[i].movie_title, buffer); fgets(buffer, SIZE, fp1); data[i].movie_rate = atoi(buffer); } fprintf(fp2, "=================================\n\t\t영화목록\t\t\n=================================\n"); for (int i = 0; i < movie_data_num; i++) { fprintf(fp2, "이름: %s\n", data[i].movie_title); fprintf(fp2, "평점: %lf\n", data[i].movie_rate); } /*for (int i = 0; i < movie_data_num; i++) { free(data[i].movie_title); }*/ }
c언어 파일 입출력을 공부하던 도중에.. 해당 메모리 해제코드 주석을 해제하면 바로 런타임 에러가 뜹니다.. 그런데 어떤걸 잘못했는지 감이 안와서.. 질문 드립니다.. ㅠㅠ
Forums:
글쎄요. free 전에 fflush(stdout);
글쎄요. free 전에 fflush(stdout); 한번 넣어 보실래요?
ㅠㅠ 안되네요..
어렵네요.. 그래도 파일에 써지긴 해서.. 목적은 이뤘지만요...
data[i].movie_title = (char*
이 부분이 문제입니다.
C언어에서 null-terminated string의 메모리 크기는 반드시
strlen
으로 얻어진 크기보다 최소 1 커야 하죠. (NUL이 들어가야 하므로)근데 되려 1 작게 했으니 탈이 나는 것임.
======
현재 구현에서
movie_title
에 할당해 줄 메모리의 크기는SIZE
보다 클 이유가 없어요.그 이상의 입력을 받을 일이 없으니까요.
뭐하러 동적 할당을 합니까?
감사합니다 제가 놓치고 있었네요..
맞습니다.. 동적할당을 써야한다고 해서 어거지로 넣긴 했습니다 ㅠㅠ
재밌는 결과네요. 애초에 strcpy에서 뻣어야 할
재밌는 결과네요. 애초에 strcpy에서 뻣어야 할 것 같은데 free() 시험에서 뻣다니 흥미롭네요.
개발환경을 좀 알려주실 수 있나요? 만약 linux라면 valgrind로 수행한 결과(물론 수정 전)도 좀 알려 주세요.
예상했던 결과입니다.
예상했던 결과입니다.
그야 프로그래머 입장에서는 실수했을 때 가능한 한 가까운 데서 에러 내고 죽는 게 편하기야 합니다만...
위 코드가
strcpy
에서 죽으려면 쓰기 권한이 없는 영역까지 달려야 하는데,고작 2바이트 넘친 걸로 그러기엔 불가능까진 아니더라도 쉽지 않죠.
사실 2바이트 정도는
malloc
의 주소 정렬을 위한 padding이 커버해 줄 수도 있는데,제시된 코드에서는 레코드 하나하나 죄다 2바이트씩 넘기고 있으니 한 번 정도는 padding을 벗어나게 됩니다.
결국 이 정도 크기의 힙 오버런은 대개
free
에서 문제가 터지게 되는데...(1)
free
에 힙 오버런을 체크하는 로직이 있거나 (디버그 환경의 경우)(2) 넘어간 데이터가 힙 공간을 관리하기 위한 메타데이터 자료구조를 덮어써 버리기 때문입니다.
컴공 전공자라면
malloc
/free
를 직접 구현해 보는 실습을 할 기회가 있는데,그걸 해 보고 나면
malloc
으로 할당받은 영역을 단 1바이트만 벗어나도free
에서 문제가 될 수 있는 이유를 체득하게 됩니다.======
free
만 하면 에러가 발생한다며 초보자들이 들고 온 코드를 살펴보면 십중팔구malloc
을 필요한 것보다 작게 한 경우입니다.흔한 실수에요.
자세한 설명 감사합니다
많이 공부하고 배우겠습니다! 댓글 너무 감사합니다!
아직 리눅스를 안써봤습니다..ㅠㅠ
개발환경은 비주얼스튜디오 2022 입니다 그리고 댓글 너무 감사합니다!
linux에서 malloc()으로 작은 공간을
linux에서 malloc()으로 작은 공간을 할당하고 테스트 해보니 아래 같은 말도 안되는 코드도 실행되네요. valgrind로 보니 오류가 있다고 나오고요. 생각외로 heap에 있는 주소 공간은 에러로 잡지를 못하네요.
반면 로컬 변수로 바꾸면 바로 잡네요. 또 웃긴게 전역 변수로 잡으면 또 못 잡네요. valgrind로도 문제없다고 나옵니다. 흥미롭네요~~
첫번째 경우는 malloc시 aligned된 크기를
첫번째 경우는 malloc시 aligned된 크기를 할당해서 4바이트를 요구해도 그보다 큰 크기를 할당합니다.
궁금해서 그런데 전역 변수로 했다는게 어떤식으로 하신건지 알 수 있을까요? 그냥 전역 변수에 malloc으로 할당한건가요?
댓글 달기