Search
Duplicate

20210104(월)

내용
42) libft list 구현
공부장소
2021/03/20 18:22
libft list 구현
libft의 보너스 문제로 list를 구현하는 것이 나왔다
typedef struct s_list { void *content; struct s_list *next; }
C
복사
일단 문제에서 위와 같은 구조체를 정의해 주었다.
void*형태의 내용을 가지고 있고 다음 리스트로의 포인터를 가지고 있다.
리스트를 구현하기위해 리스트생성→추가→삭제 순으로 구현하였다.
생성은 구조체에 정의된 사이즈만큼 동적할당을 통해 하나의 노드를 생성한 후 next포인터를 NULL로 설정해 두었다. 동적할당을 할 때에는 항상 체크를 해서 동적할당을 실패할 경우 NULL포인터를 바로 리턴시켜 주었다.
t_list *ft_lstnew(void *content) { t_list *new_node; if (!(new_node = (t_list *)malloc(sizeof(t_list)))) return (NULL); new_node->content = content; new_node->next = NULL; return (new_node); }
C
복사
아래 두 함수는 리스트의 앞, 뒤에 개체를 추가하는 함수이다. 매개변수로 리스트를 가리키는 포인터와 추가할 리스트를 받는다. 여기서 중요한 점은 **lst가 리스트에 대한 이해가 필요하다.
처음에 !lst를 통해 **lst가 NULL인지 체크를 진행한다. 이때 **lst의 의미는 *lst들이 연결되어있는 리스트의 시작위치를 가리키는 포인터라고 할 수 있다. 그렇다면 !lst의 의미는 무엇일까.
!lst는 lst가 비어있다는 뜻이 아니라 lst를 가리키는 포인터가 비어있다는 의미이다. 따라서 리스트가 메모리상에 존재하지만 이것을 가리키는 포인터가 비어있을 수 있다는 것이다. 여기서 이 포인터가 없기 때문에 *lst를 활용해 어떤 행동을 하더라도 이 함수를 나가게 되면 모든 행동이 무의미하게 되는 것이다. (힙영역에서 행동하는 것이기 때문에!)
그래서 우리가 리스트가 비어있는지 체크하기 위해서는 !*lst를 활용해야 한다! 이 의미는 *lst가 가리키는 값이 NULL인지 체크하는 것이다.
void ft_lstadd_back(t_list **lst, t_list *new) { t_list *tmp; if (!lst || !new) return ; if (!*lst) *lst = new; else { tmp = ft_lstlast(*lst); tmp->next = new; } } void ft_lstadd_front(t_list **lst, t_list *new) { if (!lst || !new) return ; new->next = *lst; *lst = new; }
C
복사
delone함수는 노드 하나를 지우는 것이다. 매개변수로 받은 리스트를 별다른 작업 없이 메모리상에서 지워주는 역할을 한다. clear함수는 리스트의 시작위치를 가리키는 포인터를 받아서 연결되어있는 모든 리스트를 지워주는 역할이다. delone함수를 호출해서 노드를 하나씩 지우고 다음 노드를 가리키는 방식으로 구현하였다.
void ft_lstdelone(t_list *lst, void (*del)(void *)) { if (!lst || !del) return ; del(lst->content); free(lst); lst = NULL; }
C
복사
void ft_lstclear(t_list **lst, void (*del)(void *)) { t_list *tmp; if (!lst || !del) return ; while (*lst) { tmp = (*lst)->next; ft_lstdelone(*lst, del); *lst = tmp; } }
C
복사
lstiter함수는 리스트를 순회하면서 각각의 값들에 매개변수로 들어온 함수를 적용시키는 것이다. 이 함수는 뒤에나오는 map함수와 비슷한 역할을 하는데 가장 큰 차이는 map함수는 새로운 리스트를 만들어서 리턴한다는 것에 있다. map 함수의 경우 lst는 배열처럼 random access가 불가능하기 때문에 현재 위치를 가리키는 포인터가 필요하기 때문에 ptr포인터를 활용해 함수를 적용한 노드를 추가할 현재 위치를 업데이트 해주었다.
void ft_lstiter(t_list *lst, void (*f)(void *)) { if (!lst || !f) return ; while (lst) { f(lst->content); lst = lst->next; } }
C
복사
t_list *ft_lstmap(t_list *lst, void *(*f)(void *), void (*del)(void *)) { t_list *new; t_list *tmp; t_list *ptr; tmp = NULL; if (!lst || !(new = ft_lstnew(f(lst->content)))) return (NULL); ptr = new; lst = lst->next; while (lst) { if (!(tmp = ft_lstnew(f(lst->content)))) { ft_lstclear(&new, del); return (NULL); } ptr->next = tmp; ptr = ptr->next; lst = lst->next; } return (new); }
C
복사