과제 유의사항
메모리 관리
GNL 과제를 진행하며 메모리 관리의 중요성을 계속해서 느꼈다. 이전까지는 메모리공간의 사용이 복잡하지 않아 기계적으로 malloc 뒤에 free를 해주었지만 이번 과제에서는 함수를 호출하며 해당 포인터를 인수로 넘겨주거나 반환값으로 넘겨주는 경우가 있어 꼼꼼하게 관리할 필요가 있었다.
backup = ft_strjoin(backup, buffer);
위와 같이 get_next_line 함수 안에서 아래의 ft_strjoin으로 backup 뒤에 buffer를 추가해주었다. 하지만 테스터에서 계속해서 Memory leak이 발생했다.
ft_strjoin.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
char *ft_strjoin(char *s1, char *s2)
{
char *result;
size_t i;
size_t j;
if (!s1)
{
s1 = (char *)malloc(1 * sizeof(char));
s1[0] = '\0';
}
if (!s1 || !s2)
return (NULL);
i = ft_strlen(s1);
j = ft_strlen(s2);
result = (char *)malloc(i + j + 1);
if (!result)
return (NULL);
ft_strlcpy(result, s1, i + 1);
ft_strlcpy(result + i, s2, j + 1);
free(s1); // 추가한 부분
return (result);
}
위 코드가 leak이 발생했던 이유는 인자로 들어온 동적할당한 포인터 s1(기존의 backup)은 이 함수에서 사용되고 반환되지 않기 때문이다. 따라서 새롭게 메모리 해제를 위한 코드를 추가해주었다.
ft_strjoin.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
char *ft_strjoin(char *s1, char *s2)
{
char *result;
size_t i;
size_t j;
if (!s1)
{
s1 = (char *)malloc(1 * sizeof(char));
s1[0] = '\0';
}
if (!s1 || !s2)
return (NULL);
i = ft_strlen(s1);
j = ft_strlen(s2);
result = (char *)malloc(i + j + 1);
if (!result)
return (NULL);
ft_strlcpy(result, s1, i + 1);
ft_strlcpy(result + i, s2, j + 1);
free(s1); // 추가한 부분
return (result);
}
나는 아래의 방법을 통해 누수가 발견되면 디버거를 사용하여 각각의 동적할당된 변수를 따라가면서 마지막으로 사용된 위치를 찾는 방법으로 메모리 관리를 수월하게 할 수 있었다.
- 검사방법
- main문 마지막에 아래 코드 삽입해서 검사
system("leaks a.out > leaks_result_temp; cat leaks_result_temp | grep leaked && rm -rf leaks_result_temp");
- valgrind 사용
- sanitizer 사용
- main문 마지막에 아래 코드 삽입해서 검사
더 공부할 내용
- 포인터 배열 개념
- 메모리 누수 확인방법 (valgrind, sanitizer 사용법)