Libft (1)

folder_open 42Seoul / 42cursus | date_range / created at

sell #42seoul #42서울 #born2code #42cursus #Libft #C #C언어

0. 시작하기 전에..

첫 포스트 작성이다. 다소 오래 전에 했던 과제라서 기억이 가물가물 하지만 일단 써보겠다.
쓰다 보니 어지간히 길어서 1, 2편으로 나눠야겠다.

1. 과제

Mandatory part

Category Content
Program name libft.a
Turn in files Makefile, ft_*.c
Makefile NAME, all, clean, fclean, re
Libft authorized n/a
Description Write your own library: a collection of functions that will be a useful tool for your cursus.

42서울을 하면서 자주 쓸 함수들을 직접 구현하는 과제이다. 과제 진행 당시 주변에서 Libft는 자주 쓰니까 잘 만들어놓으면 좋다는 말을 많이 들었다. 또 기억나는 것이, 함수들을 구현할 때 앞서 만든 함수들을 이후에 만드는 함수에 사용할 수 있었던 구조였다.

Makefile

아마 이 당시에 Makefile 작성하는데 뭐가 뭔지 하나도 모르겠던 기억이 난다. 또 기억나는게 make bonus를 입력했을 때 어떻게 해야 보너스파일까지 컴파일하는지 고민했던 것 같다. 결과적으로 if문을 통해 flag를 줘서 해결했었던 것 같다.
첫 포스팅이니까 Makefile은 이 포스트에만 올리는 것으로 하겠다. 어차피 뒤에 다 똑같다. 나중에 가면 Makefile도 꾸며보고 싶어서 점점 발전했는데, 뭔가 변경사항이 있으면 다시 올리도록 하겠다.

Makefile

NAME = libft.a
SRCS = ft_isalpha.c \
		ft_isalnum.c \
		ft_isascii.c \
		ft_isdigit.c \
		ft_memset.c \
		ft_toupper.c \
		ft_tolower.c \
		ft_strchr.c \
		ft_strrchr.c \
		ft_isprint.c \
		ft_strlen.c \
		ft_strncmp.c \
		ft_memchr.c \
		ft_bzero.c \
		ft_memcpy.c \
		ft_memmove.c \
		ft_memcmp.c \
		ft_strlcpy.c \
		ft_strlcat.c \
		ft_strnstr.c \
		ft_atoi.c \
		ft_calloc.c \
		ft_strdup.c \
		ft_substr.c \
		ft_strjoin.c \
		ft_strtrim.c \
		ft_split.c \
		ft_itoa.c \
		ft_strmapi.c \
		ft_striteri.c \
		ft_putchar_fd.c \
		ft_putstr_fd.c \
		ft_putendl_fd.c \
		ft_putnbr_fd.c
BONUSSRCS = ft_lstnew.c \
			ft_lstadd_front.c \
			ft_lstsize.c \
			ft_lstlast.c \
			ft_lstadd_back.c \
			ft_lstdelone.c \
			ft_lstclear.c \
			ft_lstiter.c \
			ft_lstmap.c
OBJS = ${SRCS:.c=.o}
BONUSOBJS = ${BONUSSRCS:.c=.o}

ifdef BONUSFLAG
	OBJSALL = ${OBJS} ${BONUSOBJS}
else
	OBJSALL = ${OBJS}
endif

CC = cc
CFLAGS = -Wall -Wextra -Werror
RM = rm -rf

all: $(NAME)

%.o: %.c
	$(CC) $(CFLAGS) -c $^ -o $@

$(NAME): $(OBJSALL)
	ar -rc $@ $^

bonus:
	make BONUSFLAG=1 all

clean:
		${RM} ${OBJS} ${BONUSOBJS}

fclean:	clean
		${RM} ${NAME}

re:		fclean all


.PHONY: clean fclean re all bonus

지금보니까 진짜 원초적인 Makefile이다. 추억이라면 추억인 듯. 그럼 이제 과제로 들어가보자.
42서울은 더럽게 불친절하다. 알아서 어떤 함수인지 찾아야 한다. 평가를 받을 때 평가자분들이 man 명령어와 함께 살펴보셨던 기억이 있다.

isalpha

알파벳인지 확인하는 함수이다.

ft_isalpha.c

int	ft_isalpha(int c)
{
	if ((97 <= c && c <= 122) || (65 <= c && c <= 90))
		return (1);
	return (0);
}

isdigit

숫자인지 확인하는 함수이다.

ft_isdigit.c

int	ft_isdigit(int c)
{
	if (48 <= c && c <= 57)
		return (1);
	return (0);
}

isalnum

알파벳 혹은 숫자인지 확인하는 함수이다.

ft_isalnum.c

static	int	ft_isalpha_clone(int c);
static	int	ft_isdigit_clone(int c);

static	int	ft_isalpha_clone(int c)
{
	if ((97 <= c && c <= 122) || (65 <= c && c <= 90))
		return (1);
	return (0);
}

static	int	ft_isdigit_clone(int c)
{
	if (48 <= c && c <= 57)
		return (1);
	return (0);
}

int	ft_isalnum(int c)
{
	if (ft_isalpha_clone(c) || ft_isdigit_clone(c))
		return (1);
	return (0);
}

왜 이렇게 구현했냐고 생각이 들겠지만 그러려니 하고 넘어가면 된다. 라 피신 끝난 지도 얼마 안되었을 때다.
앞서 말했던 이전에 구현하는 함수들을 이후에 사용하는 구조라는 것이 이런 느낌이다.

isascii

ascii 문자인지 확인하는 함수이다.

ft_isascii.c

int	ft_isascii(int c)
{
	if (0 <= c && c <= 127)
		return (1);
	return (0);
}

isprint

출력 가능한 문자인지 확인하는 함수이다. ascii 표를 확인하면 대충 출력가능해 보이는 것들이 있다.

ft_isprint.c

int	ft_isprint(int c)
{
	if (32 <= c && c <= 126)
		return (1);
	return (0);
}

strlen

라 피신에서 징하게 썼겠지만 문자열의 길이를 리턴하는 함수이다.

ft_strlen.c

size_t	ft_strlen(const char *s)
{
	size_t	count;

	count = 0;
	while (*s)
	{
		count++;
		s++;
	}
	return (count);
}

memset

특정 메모리를 특정 값으로 초기화하는 함수이다. 동적 할당된 메모리 같은 것들을 한 번에 초기화할 때 유용하다.

ft_memset.c

void	*ft_memset(void *b, int c, size_t len)
{
	size_t			i;
	unsigned char	*addr;

	i = 0;
	addr = (unsigned char *)b;
	while (i < len)
	{
		*addr++ = c;
		i++;
	}
	return ((void *)b);
}

bzero

특정 메모리를 0으로 초기화하는 함수이다. 이전에 쓴 함수를 가져오면 될 것 같다.

ft_bzero.c

void	ft_bzero(void *s, size_t n)
{
	ft_memset(s, 0, n);
}

memmove

메모리를 이동시키는 함수이다. 아마 src, dst관련해서 경우의 수를 좀 따져봐야 했던 것 같다.

ft_memmove.c

void	*ft_memmove(void *dst, const void *src, size_t len)
{
	size_t			i;
	size_t			j;
	unsigned char	*addr;

	j = 0;
	addr = dst;
	if (dst == 0 && src == 0)
		return (0);
	if (dst > src)
	{
		i = len - 1;
		while (j < len)
		{
			addr[i] = *((unsigned char *)(src + i));
			i--;
			j++;
		}
		return (dst);
	}
	else
		return (ft_memcpy(dst, src, len));
}

strlcpy

문자열을 사이즈만 큼 복사해주는 함수이다. 같은 기능을 하는 strncpy를 보완한 함수이다. 문자열 마지막에 NULL을 붙인다. 복사하는 문자여의 길이를 리턴한다.

ft_strlcpy.c

size_t	ft_strlcpy(char *dst, const char *src, size_t dstsize)
{
	size_t	src_len;
	size_t	i;

	src_len = ft_strlen(src);
	i = 0;
	if (dstsize == 0)
		return (src_len);
	else
	{
		while (*(src + i) && (i + 1 < dstsize))
		{
			dst[i] = src[i];
			i++;
		}
		dst[i] = '\0';
	}
	return (src_len);
}

strlcat

두 문자열을 붙이는 함수이다. 마찬가지로 문자열 마지막에는 NULL이 붙는다. 다만, 주어진 길이만큼까지만 문자열을 붙인다.

ft_strlcat.c

size_t	ft_strlcat(char *dst, const char *src, size_t dstsize)
{
	size_t	dst_len;
	size_t	src_len;
	size_t	i;

	i = 0;
	dst_len = ft_strlen(dst);
	src_len = ft_strlen(src);
	if (dst_len >= dstsize)
		return (src_len + dstsize);
	else
	{
		while (src[i] && (dst_len + i + 1 < dstsize))
		{
			dst[dst_len + i] = src[i];
			i++;
		}
		dst[dst_len + i] = '\0';
	}
	return (src_len + dst_len);
}

toupper

인자로 받은 알파벳을 대문자로 바꿔서 리턴하는 함수이다.

ft_toupper.c

int	ft_toupper(int c)
{
	if (97 <= c && c <= 122)
		return (c - 32);
	else
		return (c);
}

tolower

인자로 받은 알파벳을 소문자로 바꿔서 리턴하는 함수이다.

ft_tolower.c

int	ft_tolower(int c)
{
	if (65 <= c && c <= 90)
		return (c + 32);
	else
		return (c);
}

strchr

문자열 내에 일치하는 문자가 있는지 확인한다. 있다면 해당 문자 위치의 포인터를 반환, 없다면 NULL을 반환한다.

ft_strchr.c

char	*ft_strchr(const char *s, int c)
{
	c = (char)c;
	if (c == 0)
		return ((char *)(s + ft_strlen(s)));
	while (*s)
	{
		if (*s == c)
			return ((char *)s);
		s++;
	}
	return (0);
}

strrchr

문자열의 뒤에서부터 일치하는 문자가 있는지 확인한다.

ft_strrchr.c

char	*ft_strrchr(const char *s, int c)
{
	const char	*addr;

	c = (char)c;
	if (c == 0)
		return ((char *)(s + ft_strlen(s)));
	addr = s + ft_strlen(s) - 1;
	while (*addr)
	{
		if (*addr == c)
			return ((char *)addr);
		if (addr == s)
			break ;
		addr--;
	}
	return (0);
}

strncmp

두 문자열을 특정 길이만큼 비교한다. 주어진 길이가 문자열보다 크다면 문자열의 길이까지만 비교한다.

ft_strncmp.c

int	ft_strncmp(const char *s1, const char *s2, size_t n)
{
	size_t	i;

	i = 0;
	while (((unsigned char)*s1 || (unsigned char)*s2) && i < n)
	{
		if ((unsigned char)*s1 != (unsigned char)*s2)
			return ((unsigned char)*s1 - (unsigned char)*s2);
		s1++;
		s2++;
		i++;
	}
	return (0);
}

memchr

strchr과 비슷하지만, 문자열 만이 아닌 특정 메모리 안에서 특정 값을 검색하여 해당 주소를 리턴한다.

ft_memchr.c

void	*ft_memchr(const void *s, int c, size_t n)
{
	size_t			i;
	unsigned char	chr;
	unsigned char	*s_copy;

	i = 0;
	chr = (unsigned char)c;
	s_copy = (unsigned char *)s;
	while (i < n)
	{
		if (s_copy[i] == chr)
			return (s_copy + i);
		i++;
	}
	return (0);
}

memcmp

특정 두 메모리의 값을 비교한다.

ft_memcmp.c

int	ft_memcmp(const void *s1, const void *s2, size_t n)
{
	size_t	i;

	i = 0;
	while (i < n)
	{
		if (*(unsigned char *)(s1 + i) != *(unsigned char *)(s2 + i))
			return (*(unsigned char *)(s1 + i) - *(unsigned char *)(s2 + i));
		i++;
	}
	return (0);
}

strnstr

문자열 haystack에서 길이 len만큼 needle이 있는지 검사한다.

ft_strnstr.c

char	*ft_strnstr(const char *haystack, const char *needle, size_t len)
{
	size_t	i;
	size_t	j;

	i = 0;
	if (!(*needle))
		return ((char *)haystack);
	while (i < len && *(haystack + i))
	{
		j = 0;
		if (*(haystack + i) == *needle)
		{
			while (j + i < len && *(needle + j) && *(haystack + i + j))
			{
				if (*(needle + j) != *(haystack + i + j))
					break ;
				j++;
			}
			if (j == ft_strlen(needle))
				return ((char *)(haystack + i));
		}
		i++;
	}
	return (0);
}

atoi

문자열을 정수로 변환한다.

ft_atoi.c

static int	get_sign(char str, int *i)
{
	if (str == '-' || str == '+')
	{
		(*i)++;
		if (str == '-')
			return (-1);
		return (1);
	}
	if ('0' <= str && str <= '9')
		return (1);
	return (0);
}

int	ft_atoi(const char *str)
{
	int	i;
	int	sign;
	int	num;

	i = 0;
	num = 0;
	sign = 1;
	while ((str[i] == ' ') || (9 <= str[i] && str[i] <= 13))
		i++;
	sign = get_sign(str[i], &i);
	while ('0' <= str[i] && str[i] <= '9')
		num = num * 10 + (str[i++] - 48);
	return (sign * num);
}



아래 두 함수는 구현하기 위해 malloc함수가 허용된다. 메모리를 할당할 때는 항상 누수에 주의하자.

  • calloc
  • strdup

calloc

특정 사이즈만큼 메모리를 할당한다. malloc과 비슷하지만, 요소를 0으로 초기화한다는 특징이 있다.

ft_calloc.c

void	*ft_calloc(size_t count, size_t size)
{
	void	*addr;

	if (size * count > SIZE_MAX)
		return (0);
	addr = malloc(size * count);
	if (!(addr))
		return (0);
	ft_bzero(addr, count * size);
	return (addr);
}

strdup

새로 동적할당된 메모리에 문자열을 복사하여 리턴하는 함수이다.

ft_strdup.c

char	*ft_strdup(const char *s1)
{
	int		s1_len;
	int		i;
	char	*return_arr;

	s1_len = ft_strlen(s1);
	i = -1;
	return_arr = (char *)malloc(sizeof(char) * (s1_len + 1));
	if (!return_arr)
		return (0);
	while (s1[++i])
		return_arr[i] = s1[i];
	return_arr[i] = '\0';
	return (return_arr);
}

substr

주어진 문자열으로부터 start ~ start + len 까지 문자열을 추출(새로 할당)하는 함수이다.

ft_substr.c

char	*ft_substr(char const *s, unsigned int start, size_t len)
{
	char	*str;
	size_t	i;
	size_t	to_substr_len;
	size_t	s_len;

	s_len = ft_strlen(s);
	i = 0;
	to_substr_len = 0;
	if (s_len < start)
		to_substr_len = 0;
	else if (start + len > s_len)
		to_substr_len = s_len - start;
	else
		to_substr_len = len;
	str = (char *)malloc(sizeof(char) * (to_substr_len + 1));
	if (!str)
		return (0);
	while (i < to_substr_len && s[start + i])
	{
		str[i] = s[start + i];
		i++;
	}
	str[i] = '\0';
	return (str);
}

strtrim

문자열 앞 뒤의 공백을 제거하는 함수이다. 새로운 문자열로 반환한다.

ft_strtrim.c

static int	get_end_addr(char const *s1, char const *set)
{
	size_t		len;

	if (!(*s1))
		return (0);
	len = ft_strlen(s1);
	if (len)
		len--;
	else
		return (0);
	while (*(s1 + len))
	{
		if (!ft_strchr(set, *(s1 + len)))
			break ;
		else
			len--;
	}
	return (len + 1);
}

static int	get_start_addr(char const *s1, char const *set)
{
	size_t		i;

	i = 0;
	if (!(*s1))
		return (0);
	while (*s1)
	{
		if (ft_strchr(set, *s1) == 0)
			break ;
		else
			i++;
		s1++;
	}
	return (i);
}

char	*ft_strtrim(char const *s1, char const *set)
{
	int		start;
	int		end;
	char	*return_str;
	int		i;

	start = get_start_addr(s1, set);
	end = get_end_addr(s1, set);
	if (start > end)
	{
		return_str = ft_strdup("");
		return (return_str);
	}
	return_str = (char *)malloc(sizeof(char) * (end - start + 1));
	if (!return_str)
		return (0);
	i = -1;
	while (start < end)
	{
		return_str[++i] = s1[start];
		start++;
	}
	return_str[i + 1] = '\0';
	return (return_str);
}

split

대망의 split이다. 만약 libft에서 어찌저찌 넘어갈 생각이어도 통과한 이후에 꼭 제대로 만들어놓는 것을 추천한다. 앞으로 많이 쓸 거다.
주어진 문자열을 특정 문자를 기준으로 잘라서 반환하는 함수이다.

ft_split.c

static int	get_word_len(char const *str, char c)
{
	int	str_index;
	int	count;

	str_index = 0;
	count = 0;
	while (str[str_index] && str[str_index] == c)
		str_index++;
	while (str[str_index] && str[str_index] != c)
	{
		str_index++;
		count++;
	}
	return (count);
}

static int	get_word_count(char const *str, char c)
{
	int	str_index;
	int	word_count;

	str_index = 0;
	word_count = 0;
	while (str[str_index])
	{
		while (str[str_index] && str[str_index] == c)
			str_index++;
		if (str[str_index])
			word_count++;
		while (str[str_index] && str[str_index] != c)
			str_index++;
	}
	return (word_count);
}

static char	*arr_input(char const *str, char c, int word_len)
{
	int		arr_index;
	int		str_index;
	char	*arr;

	arr_index = 0;
	str_index = 0;
	arr = (char *)malloc(sizeof(char) * (word_len + 1));
	if (!arr)
		return (0);
	arr[word_len] = 0;
	while (str[str_index] && str[str_index] == c)
		(str_index)++;
	while (str[str_index] && str[str_index] != c)
	{
		arr[arr_index] = str[str_index];
		arr_index++;
		str_index++;
	}
	return (arr);
}

char	**free_all(char **arr, int word_count)
{
	int	i;

	i = -1;
	while (++i < word_count)
	{
		if (!arr[i])
		{
			i = -1;
			while (++i < word_count)
				free(arr[i]);
			free(arr);
			return (0);
		}
	}
	return (arr);
}

char	**ft_split(char const *s, char c)
{
	char	**arr;
	int		word_count;
	int		word_len;
	int		i;
	int		str_index;

	word_count = get_word_count(s, c);
	arr = (char **)malloc(sizeof(char *) * (word_count + 1));
	if (!arr)
		return (0);
	i = -1;
	str_index = 0;
	while (++i < word_count)
	{
		word_len = get_word_len(&s[str_index], c);
		arr[i] = arr_input(&s[str_index], c, word_len);
		while (s[str_index] == c)
			str_index++;
		str_index += word_len + 1;
	}
	arr[i] = 0;
	arr = free_all(arr, word_count);
	return (arr);
}

itoa

정수를 문자열로 바꿔서 변환하는 함수이다.

ft_itoa.c

static int	get_digit_count(long long n)
{
	int			count;
	long long	num;

	count = 1;
	num = n;
	if (n < 0)
	{
		count++;
		num = -n;
	}
	while (num / 10 > 0)
	{
		num /= 10;
		count++;
	}
	return (count);
}

char	*ft_itoa(int n)
{
	long long	num;
	char		*str;
	int			digit;

	digit = get_digit_count((long long)n);
	str = (char *)ft_calloc(digit + 1, sizeof(char));
	if (!str)
		return (0);
	if (n < 0)
	{
		num = -((long long)n);
		str[0] = '-';
	}
	else
		num = (long long)n;
	str[digit] = '\0';
	while (--digit >= 0)
	{
		if (str[digit] == '-')
			break ;
		str[digit] = num % 10 + '0';
		num /= 10;
	}
	return (str);
}

strmapi

주어진 문자열을 순회하며 각각 주어진 함수를 적용시켜 반환하는 함수이다.

ft_strmapi.c

char	*ft_strmapi(char const *s, char (*f)(unsigned int, char))
{
	char	*str;
	size_t	len;
	size_t	i;

	if (!s || !f)
		return (0);
	len = ft_strlen(s);
	str = (char *)malloc(sizeof(char) * (len + 1));
	if (!str)
		return (0);
	str[len] = '\0';
	i = 0;
	while (i < len)
	{
		str[i] = f(i, s[i]);
		i++;
	}
	return (str);
}

striteri

주어진 문자열을 순회하며 각각 함수만 적용시킨다.

ft_striteri.c

void	ft_striteri(char *s, void (*f)(unsigned int, char*))
{
	size_t	len;
	size_t	i;

	len = ft_strlen(s);
	i = 0;
	while (i < len)
	{
		f(i, &s[i]);
		i++;
	}
}

ft_putchar_fd

파일 디스크립터를 받아 문자를 적는 함수이다.

ft_putchar_fd.c

void	ft_putchar_fd(char c, int fd)
{
	write(fd, &c, 1);
}

ft_putstr_fd

파일 디스크립터를 받아 문자열을 적는 함수이다.

ft_putstr_fd.c

void	ft_putstr_fd(char *s, int fd)
{
	size_t	i;
	size_t	len;

	i = 0;
	len = ft_strlen(s);
	while (i < len)
	{
		write(fd, &s[i], 1);
		i++;
	}
}

지금 생각해보면 이 것도 ft_putchar_fd 를 재탕하면 됐는데 말이다.

ft_putendl_fd

파일 디스크립터를 받아 주어진 문자열을 개행과 함께 적는 함수이다.

ft_putendl_fd.c

void	ft_putendl_fd(char *s, int fd)
{
	ft_putstr_fd(s, fd);
	write(fd, "\n", 1);
}

ft_putnbr_fd

파일 디스크립터를 받아 숫자를 적는 함수이다.

ft_putnbr_fd.c

void	ft_putnbr_fd(int n, int fd)
{
	if (n >= 0)
	{
		if (n >= 10)
		{
			ft_putnbr_fd(n / 10, fd);
			ft_putnbr_fd(n % 10, fd);
		}
		else
			ft_putchar_fd(n + '0', fd);
	}
	else
	{
		if (n == -2147483648)
			write(fd, "-2147483648", 11);
		else
		{
			ft_putchar_fd('-', fd);
			ft_putnbr_fd(-1 * n, fd);
		}
	}
}