20210111(월) - jungcow/42Cursus GitHub Wiki
- 2021-01-11(월)
- 16:00 ~ 20:00 / 22:30 ~ 03:30 (집)
- get_next_line의 mandatory와 bonus 구현
- discord를 통해 seunghoh님과의 질의응답(get_next_line 초기화 관련 질의응답)
- 메모리 릭을 철저히 관리
- bonus까지 구현
http://13.125.198.2:3000/jungwkim/get_next_line.git
-
Prototype :
int get_next_line(int fd, char **line); -
Parameters
- fd : 프로그램이 읽을 파일 디스크립터(file descripter)
- 한 줄을 읽은 후 넣을 문자열의 주소
-
Return value
- 1: 한 라인이 읽혔을 때.
- 0: EOF에 도달했을 때
- -1: 에러가 발생했을 때
-
External functs
- read() ->
<unistd.h> - malloc() ->
<stdlib.h> - free() ->
<ftdlib.h>
- read() ->
-
Description
파일 디스크럽터로부터 읽어 온 하나의 라인(newline 없이)을 반환하는 함수 작성
int main(void)
{
char *line;
int fd;
int fd2;
int fd3;
int fd4;
int len;
fd = open("example.txt", O_CREAT | O_RDONLY, 0777);
fd2 = open("example2.txt", O_CREAT | O_RDONLY, 0777);
fd3 = open("example3.txt", O_CREAT | O_RDONLY, 0777);
fd4 = open("example4.txt", O_CREAT | O_RDONLY, 0777);
if (fd == -1)
return (1);
fd = 0;
//fd2 = 0;
while ((len = get_next_line(fd, &line)) == 1)
{
printf("%d, %s\n", len, line);
free(line);
}
printf("%d, %s\n", len, line);
while ((len = get_next_line(fd2, &line)) == 1)
{
printf("%d, %s\n", len, line);
free(line);
}
printf("%d, %s\n", len, line);
while ((len = get_next_line(fd3, &line)) == 1)
{
printf("%d, %s\n", len, line);
free(line);
}
printf("%d, %s\n", len, line);
while ((len = get_next_line(fd4, &line)) == 1)
{
printf("%d, %s\n", len, line);
free(line);
}
printf("%d, %s\n", len, line);
/*
line = "abcde";
len = get_next_line(fd, &line);
printf("%d, %s\n", len, line);
free(line);
len = get_next_line(fd2, &line);
printf("%d, %s\n", len, line);
free(line);
len = get_next_line(fd3, &line);
printf("%d, %s\n", len, line);
free(line);
len = get_next_line(fd2, &line);
printf("%d, %s\n", len, line);
free(line);
len = get_next_line(fd3, &line);
printf("%d, %s\n", len, line);
free(line);*/
close(fd);
close(fd2);
close(fd3);
return (0);
}/** 1. 여러개의 fd가 섞여서 들어올 때** ** 2. fd가 차례대로 파일의 끝까지 읽어들일 때.
- heap 영역이 여러가지 버퍼 사이즈가 설정되어있는 리스트로 이루어진 구조로 되어있고,
- malloc을 함수에서 호출하게 되면, heap영역에서 해당하는 버퍼사이즈가 설정되어있는 영역을 할당해준다.
- 이 때, heap영역에서 해당하는 버퍼사이즈가 없을 경우(동적할당하려는 버퍼사이즈가 훨씬 큰 경우) 리스트들을 엮어서 해당하는 버퍼사이즈로 만들고 해당하는 메모리를 할당해준다.
- free()를 해준다는 의미는 다시 heap영역에서 할당가능한 리스트로 되돌아가게 만들어줌을 의미함.
- heap 영역에 동적할당을 시켜준 데이터들을 free를 안시켜줄시 메모리가 함수가 끝나도 그대로 남아있어, 메모리 릭이 나게 된다.
- 모든 동적할당한 데이터들은 사용한 후에는 반드시 free를 해주어야 한다.
- main함수에서
while(1);로 함수가 끝나는 것을 강제로 막아준다. -
./a.out으로 프로그램을 실행 - 다른 터미널을 열어서
leaks a.out명령어를 친다. - 메모리 릭이 나는 주솟값과 그 안에 들어있는 데이터 값을 얻을 수 있다.
- malloc이 실패했거나, read()에서 오류가 나서 -1을 반환할 때, 지금 fd를 포함한 구조체 포인터는 물론, 모든 구조체 포인터들을 free()시켜준다.
void clear_buffer(t_backup **backup)
{
t_backup *ptr;
t_backup *tmp;
ptr = *backup;
while (ptr)
{
tmp = ptr->next;
free(ptr->str);
free(ptr->tmp);
free(ptr);
ptr = tmp;
}
ptr = NULL;
}- 링크드 리스트에서 해당 fd에 해당하는 구조체를 삭제하고 전의 구조체와
*next를 연결해준다.
void del_buffer(int fd, t_backup **backup)
{
t_backup *ptr;
t_backup *before;
ptr = *backup;
if (ptr->fd == fd)
{
*backup = ptr->next;
free(ptr->tmp);
free(ptr);
return ;
}
while (ptr)
{
if (ptr->fd == fd)
{
before->next = ptr->next;
free(ptr->tmp);
free(ptr);
break ;
}
before = ptr;
ptr = ptr->next;
}
}- 아래 ptr은 잠시 str의 데이터들을 받아놓는 임시 저장소 기능을 한다.
- 이 때, ptr은 이 함수에서 기능을 다 쓰기 때문에 여기서 free를 해주어야 했는데 해주지 않아 메모리 릭이 계속 났었다.
int alloc_line(char **line, t_backup *backup, ssize_t flag)
{
char *ptr;
backup->sum -= flag + 1;
if (!(*line = ft_strndup(backup->str, flag)))
return (0);
if (!(ptr = ft_strndup(backup->str + flag + 1, backup->sum)))
return (0);
free(backup->str);
if (!(backup->tmp = (char *)malloc(sizeof(char) * backup->sum)))
return (0);
ft_memcpy(backup->tmp, ptr, backup->sum);
free(ptr);
return (1);
}- main함수에서 get_next_line을 호출하여 line에 데이터들을 담은 후에 바로 printf를 한후 다음 루프로 넘어가지 말고 그전에 line을 free를 해주어야 한다.
- testcase를 짤 때의 문제점이었고, testcase를 구성할 때, 이런 메모리 leaks가 나는 것을 조심하자.
> ./a.out
fd : 3
hello
after gnl line: hello
world
after gnl line: world
greate world babvy
after gnl line: greate world babvy
soso agasdf ------------------------------> 여기서 ctrl + D를 눌렀다.
after gnl line: soso agasdf
afger gnl line: --------------------------> 그러자 여기서 하나가 더 생긴다.(문제점)
- 위와 같은 결과는 당연한 것.
- 개행 문자가 있을 시
after gnl line:후에line이 출력 된다. - 그 이후 남은 문자는 EOF 하나뿐이다. 이 때, get_next_line은 0을 반환.
- 따라서
after gnl line:후에 line에는 널값만 들어오게 된다.
- get_next_line 함수를 다루면서 memory leak관련해서 많이 배우게 되었다.
- malloc과 free 그리고 포인터를 많이 다루는 거다 보니까 데이터가 할당된 heap영역의 주소를 free해서 다른 프로그램들이 할당 가능한 상태로 만들어줘야 메모리 릭이 나지 않는다는 것을 알았다.
- get_next_line을 끝내고, 든 생각은 잘하는 사람이 옆에 있냐 없냐가 큰 차이가 있었다는 것이다. 동료평가가 잘하는 사람에게 물어보며 학습하는 방식으로 알고 있다. 하지만, 있고 없고의 차이가 너무 큰 것에 대해 자신에게 큰 실망을 하게 된 것 같다.
- ft_printf 문제를 읽고 문제 이해하기
- testcase를 구성하여 빠르게 로직 짜기
- get_next_line을 다시 한번 처음부터 끝까지 짜보기
- notion에 핵심 기능을 정리하기
- notion에 로직 정리하기
- notion에 예외 처리 사항 정리하기