Taking baby-developer steps

2022.02.03. gnl 메모리 이중 할당해제, KO 케이스들 본문

Logs/학습 log

2022.02.03. gnl 메모리 이중 할당해제, KO 케이스들

Surin Lee 2022. 2. 3. 14:52

오늘 목표

  • gnlTest 테스터 Mandatory Part통과
    • KO 뜨는 케이스 잡기
    • 포인터 할당 해제 관련 문제 해결

오늘 한 일

  • 할당된 메모리 이중 해제 관련 문제 해결

드디어 "error : pointer being freed was not allocated"라는 메세지가 모두 없어졌다!

어제 알게된 aliasing(두개 이상의 포인터가 같은 메모리 주소를 가리키는 현상)으로 인해 이미 할당 해제된 메모리 공간을 가리키는 댕글링 포인터가 발생하게 되었고, 이로 인해 이중 할당해제 문제까지 발생했던것과 비슷한 맥락에서,

한줄이 완성 될 때까지(eof 혹은 '\n'를 만날 때까지) 버퍼에 남아있는 문자열을 저장해두는 buffers[fd]포인터에서, 마지막 호출 할 시에, 이번 호출에서 반환할 a_line의 주소값과 동일한 메모리를 가리키는 때가 있음을 확인하였고, 빠른 해결 법으로 a_line을 반환하기 전, buffers[fd]가 가리키는 주소값과 a_line이 가리키는 주소값이 같을 경우에, buffers[fd]가 더이상 정보를 저장할 필요가 없어지므로 그 주소값을 0x0을 가리키게 함으로서 문제를 해결하였다.

마지막으로 유효한(해당 fd에서 마지막으로 읽어올 내용이 남아있는) 호출에서 buffers[fd]를 널 포인터를 가리키게 하면, 그 다음번 호출(호출 되더라도 읽어오는 내용이 없어야 하고 아무 일도 없어야한다.(메모리 누수나 섣부른 해제도 일어나면 안된다) read_from_fd()함수의 내부에서는 read()함수가 해당 fd에 대해서 반환하는 값이  -1 이거나 0일 경우, 동적할당 했던 curr_buffer를 할당 해제하는 역할까지 한다.)에서 105~106번째 줄에 의해 의도한 대로 널포인터를 반환하며 함수가 종료된다.

 

  • KO 뜨는 케이스 잡기

84줄에서 exit()함수만 쓸수 있어도... 바로 ko 케이스들을 잡을 수 있는데.. 아쉽게도 exit()함수의 사용이 금지 되어있으니, 애초에 해당 fd에 해당하는 곳으로부터 모든 텍스트를 읽은 후 gnl을 호출하면  바로 종료될 수 있도록 로직을 짜봐야겠다.

-> 더 이상 읽어들일게 없을 때 NULL 포인터를 반환해버려야하는데, ""(\0가 들어있는 1만큼 공간을 가진 포인터)가 몇몇 케이스에서 반환되는것이 확인되어서, 110~111라인처럼 get_a_line함수에서 반환한 문자열의 크기가 1일때 (이 경우 빈 문자열을 반환한 경우에만 해당된다. (get_a_line 함수 내부에서 malloc시 문자열임을 고려하여 '반환할 내용' + '\0를 넣을 공간(1 : 널 터미네이트)'만큼 할당 하기 때문) 이렇게 문제를 해결하고 나니 좀 덕지덕지 붙은 부분도 생겼을 뿐더러, get_next_line()과 get_a_line()이 25줄을 넘어가는 불상사가 발생했다.. norm 규정을 맞춰야하기 때문에.. 그리고 이렇게 if문 덕지덕지인 코드 보다는 애초에 로직에서 거를 수 있어야 좋을것 같아서 내일은 norm 규정에 맞게 리팩토링을 할 예정이다.

allowed function : read(), malloc(), free() 3개만 사용해서 get_next_lin.c mandatory part 구현 완료!

  • gnlTest 테스터 Mandatory Part통과!

메모

학습 로그를 블로그에 쓰기 시작한 이후, 처음으로 오늘 계획을 모두 완수한 날이었다.

비록 norm 규정(한 함수는 25줄 이하로 작성되야한다)을 위배하긴 했지만 내일 리팩토링으로 2~3줄 정도만 줄이면 될 것같다.(그게 아주 쉬운일은 아니겠지만..!)

이번에 메모리 관련해서 디버깅을 엄청 고민하며 해보면서 

포인터를 사용할 때 좀 더 계획을 세워두고 선언해 가며 써야겠다는 생각을 했다.

마치 한 함수가 한 역할만을 하듯이, 한 포인터도 하나의 역할만 부여해서 사용해야겠다.

사실 한 함수의 길이가 25라인을 넘을 수 없고, 선언과 동시에 초기화 할수도 없어서 잠깐 사용할 temp 같은 포인터는 선언하기 힘들어서 꼭 필요할 때가 아니라면 잠깐 아무것도 할당되어있지 않은 포인터를 데려와서 썼는데, 그럴 경우 다 쓰고 나면 꼭 NULL을 가리키게해서 하나 이상의 포인터가 동일한 주소를 가리키게 하는 일은 지양해야 하며, 그 상태로 둘중 하나의 포인터로 해당 메모리 주소를 할당해제하는 일은 절대 일어나지 않게 해야겠다.

 

Comments