prefix로 lst가 붙은 ft함수들은 연결리스트 관련 함수들이다.
연결 리스트는 노드들의 집합이므로 실제로는 노드의 구조체만 정의하면 된다.
따라서 과제에서는 헤더에 아래와 같이 노드의 구조체를 정의하도록 안내된다.
typedef struct s_list
{
void *content;
struct s_list *next;
} t_list;
구조체 노드로 정의한 연결리스트를 나중에 이중포인터로 받아오는데, 처음에는 이해가 되지 않아 확실하게 정리한다.
먼저 ft_lstnew의 함수 코드는 아래와 같다. ft_lstnew를 이용하여 새 노드를 만들 수 있다.
#include "libft.h"
t_list *ft_lstnew(void *content)
{
t_list *new;
new = (t_list *)malloc(sizeof(t_list));
if (new == NULL)
return (NULL);
new->content = content;
new->next = NULL;
return (new);
}
새 노드만 만드는 것이고, 새 노드는 다른 함수에서 연결리스트에 추가하게 될 것이다.
노드를 추가하는 모습은 다음과 같다.
ft_lstadd_front의 함수 코드는 아래와 같다.
맨 앞쪽에 리스트를 만들어 추가하므로 다음 주소값만 담는 것이 아니라 시작 지점도 알아야 한다는 것이 중요하다.
ft_lstadd_front함수는 리턴값이 없다. 따라서 마지막에 *lst = new를 통해서 헤더가 첫 노드를 잡을 수 있게 한다.
이 작업을 위해 이중포인터 파라미터를 갖는다.
첫 번째 매개변수 **lst는 첫 번째 노드의 포인터다. 이중포인터인 것은 *lst에 저장되는 값이 연결된 리스트들의 첫값을 담고 있기 때문이다.
#include "libft.h"
void ft_lstadd_front(t_list **lst, t_list *new)
{
if ((lst == NULL) || (new == NULL))
return ;
new->next = *lst;
*lst = new;
}
이중포인터로 넘겨받은 **lst가 첫번째 노드를 잡고 있다.
예시로 설명해보자면 연결리스트가 NODE1->NODE2->NODE3로 구성되어있다면
**lst는 NODE1의 주소이며, *lst는 NODE1다.
new->next = *lst; 의 작업을 통해 new라는 리스트 뒤에 *lst에 저장되어있던 NODE1이 연결되었다.
그러나 아직 연결리스트의 시작노드가 new인 것은 아니다.
*lst = new; 의 작업을 통해 연결리스트의 시작노드가 new인 것을 명시하기 위해 *lst에 new를 담는다.
이중포인터의 활용법을 쉽게 이해하기 위해 LaPiscine 때 작성했던 ft_range와 ft_ultimate_range를 비교해보겠다.
(각각은 C07의 ex01, ex02의 과제였다.)
#include <stdlib.h>
int *ft_range(int min, int max)
{
int i;
int *arr;
if (min >= max)
return (NULL);
arr = (int*)malloc(sizeof(int) * (max - min));
i = 0;
while (min < max)
{
arr[i] = min;
min++;
i++;
}
return (arr);
}
ft_range에서는 배열을 반환하지만 ft_ultimate_range에서는 배열을 반환하지 않는다.
따라서 ft_ultimate_range에서도 이중포인터를 받는다. int 배열인 **range를 받는다.
이때 값을 바꿔주기 위해 ft_ultimate_range는 생성한 배열을 매개변수로 받은 *range에 넣어준다.
만약 파라미터가 int *라면 참조를 통해 int값은 바꿔줄 수 있겠으나 배열은 넘겨주지 못한다.
#include <stdlib.h>
int ft_ultimate_range(int **range, int min, int max)
{
int i;
int *arr;
if (min >= max)
return (NULL);
arr = (int*)malloc(sizeof(int) * (max - min));
if (!arr)
return (-1);
i = 0;
while (min < max)
{
arr[i] = min;
min++;
i++;
}
*range = arr;
return (i);
}
Moulinette의 메인함수에서 함수 사용시에 int*타입의 range를 함수에 매개변수로 &range 같은 식으로 넘겨줄 것이다.
아래의 실행 예를 본다면 새로 만든 정수형 포인터 변수 d를 함수에 넘겨주기 위해 &d를 사용했다.
#include <stdio.h>
int main(void)
{
int a = 5;
int b = 9;
// ft_range
int *c = ft_range(a, b);
for (int i=0;i<4;i++)
printf("%d ", c[i]);
printf("\n");
// ft_ultimate_range
int *d;
printf("%d\n", ft_ultimate_range(&d, a, b));
for (int i=0;i<4;i++)
printf("%d ", d[i]);
// printf("%ls\n", ft_range(a, b)); // 아무것도 안 나옴
// printf("%ls\n", d); // 아무것도 안 나옴
return (0);
}
// Console
5 6 7 8
4
5 6 7 8
위의 메인함수에서 만약 ft_ultimate_range 함수에 &d가 아닌 d로 넘겨준다면 아래와 같은 오류가 발생한다.
ft_ultimate_range를 정의할 때 파라미터로 이중포인터를 받는다고 정의했기 때문이다.
/workspace/C_Test/src/main.c: In function ‘main’:
/workspace/C_Test/src/main.c:58:35: warning: passing argument 1 of ‘ft_ultimate_range’ from incompatible pointer type [-Wincompatible-pointer-types]
58 | printf("%d\n", ft_ultimate_range(d, a, b));
| ^
| |
| int *
/workspace/C_Test/src/main.c:24:30: note: expected ‘int **’ but argument is of type ‘int *’
24 | int ft_ultimate_range(int **range, int min, int max)
| ~~~~~~^~~~~
5 6 7 8
세그멘테이션 오류 (core dumped)
참고로 함수 매개변수로 포인터변수를 받을 때엔 넘겨줄 때도 주소값을 넘겨줘야 한다.
예시를 들기 위해 LaPiscine 때 작성했던 ft_swap을 들고 왔다.
(C01의 ex02의 과제였다.)
void ft_swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
ft_swap은 void형 함수임에도 불구하고 함수를 실행하면 a와 b가 호출한 함수 단에서도 바뀌어 있다.
이유는 포인터변수로 받아와 직접 대입하여 값을 바꿔줬기 때문이다.
아래 메인함수에서 호출은 ft_swap(&a, &b)로 했다.
#include <stdio.h>
int main(void)
{
int a = 5;
int b = 7;
printf("%d\n", a); // 5
printf("%d\n", b); // 7
/* warning: format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘int’ */
// printf("%p\n", a);
// printf("%p\n", b);
/* warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int *’ */
// printf("%ls\n", &a);
// printf("%ls\n", &b);
printf("%p\n", &a); // 0x7fff5f63c940
printf("%p\n", &b); // 0x7fff5f63c944
/* warning: passing argument 1 of ‘swap’ makes pointer from integer without a cast */
// swap(a, b);
swap(&a, &b);
printf("%d\n", a); // 7
printf("%d\n", b); // 5
return (0);
}
출처
연결 리스트
코딩도장 "74.1 연결 리스트 구조체 만들고 사용하기", dojang.io/mod/page/view.php?id=645
코딩도장 "74.2 노드 추가 함수 만들기", dojang.io/mod/page/view.php?id=646
'Activities > 공부' 카테고리의 다른 글
[42Seoul] ft_server 개념 - Docker, Devian, Nginx, MySQL, phpMyAdmin, Wordpress, HTTPS (0) | 2021.03.25 |
---|---|
[42Seoul] get_next_line 과제 - 배경지식 정리 (1) | 2021.01.08 |
국토연구원 X 데이콘 주최 <국토도시 빅데이터 윈터스쿨>에 참여하다 (0) | 2020.01.01 |
SW 테스트 전문가 (CSTS) 자격시험 합격 수기 (2) | 2019.11.30 |
비전공자의 2019년 정보처리기사 합격 수기 (0) | 2019.11.23 |
댓글