libc 함수 구현해보기

koeyhoyh·2022년 3월 7일
0

42Seoul

목록 보기
5/11

2022/03/07 ~
// 개포 1클러스터 작성

C89 표준으로, restrict 제한자는 적용하지 않고 작성했습니다.


설명 중, 가장 눈에 띄던 문구

복잡한 함수를 구현할 때, sub 함수들에 대해 static을 붙여 library에 포함되지 않게 해라....?

static 함수는, 해당 c 파일 안에서만 동작하고 나머지 파일에서는 동작하지 않는다. 함수의 중복 컴파일 문제도 피하고, 필요없는 함수를 라이브러리에 포함하지도 않아 조금 더 효율적으로 프로그래밍 하는 방법이라고 생각된다!!!

참고 : https://dojang.io/mod/page/view.php?id=691 / 정적 함수, 정적 변수


알아야 할 기초 지식들.

size_t 자료형

: 컴퓨터에서 표현할 수 있는 가장 큰 unsigned 자료형

32bit = unsigned int
64bit = unsigned long long

stddef.h, string.h, stdlib.h, stdio.h 헤더를 이용해 사용할 수 있다.
size_t is defined in various headers: stddef.h, string.h, stdlib.h, and stdio.h. Including any one of them is enough to use size_t in your code.
그러나, 다른 함수들이 필요 없다면 stddef.h 를 사용하는 것이 가장 효율적일 것이다.

size_t 자료형을 사용하는 이유는?

어느 곳, 어느 시스템에서도 변수에 고정적인 크기를 할당하고 싶어서 size_t 자료형을 사용한다.

size_t는 일반적으로 unsigned int / unsigned long long 형으로 선언됩니다.
정확한 선언은 "sys/types.h" 파일을 보시면 아실 수 있고요.
변수를 선언할때는 그 변수가 갖고자 하는 값의 범위를 미리 생각해서 선언합니다.
특정 자료형 (ex/ unsigned int)를 사용하게 되면 컴파일 하는 시스템마다 변수의 크기가 달라지므로
size_t와 같은 고정된 변수를 사용하는 것입니다.
따라서 어느 시스템에서나 size_t의 크기는 같은 것이지요.

restrict 자료형은 무엇인가??

restrict 포인터는 메모리 접근에 관련된 최적화 기능입니다. (C99 표준)

각 포인터가 서로 다른 메모리 공간을 가리키고 있고, 다른 곳에서 접근하지 않으니 컴파일러가 최적화를 하라는 뜻입니다.



여기서는 프로그래머가 알려준 대로 a, b, x가 서로 다른 메모리 공간을 가리키고 있다고 보고 x를 역참조하여 값을 가져오는 mov (%rdx),%eax 명령을 한 번 줄이게 됩니다. 즉, 다른 메모리 공간이므로 이전 명령어의 결과가 확실히 적용되는지는 알 필요 없이 값을 그대로 사용합니다.

참고 : https://dojang.io/mod/page/view.php?id=760

makefile

@ 컴파일할 때 붙이면, 좌르르르ㅡㄹㄱ 안뜬다.
그 후,
@echo ~ 하면 된다!


ft_substr

문자열 s[start] 부터 끝까지의 길이가 len보다 작다면?

항상 필요한 만큼만 메모리 할당을 하는 것이 좋을 것 같다.
-> 예외처리로 따로 빼주어야겠다.

if (s_size < len)
		str = (char *)malloc(sizeof(char) * (s_size + 1));
	else
		str = (char *)malloc(sizeof(char) * (len + 1));

이 정도 해주었다.

const 위치에 따른 기능!!
상수 포인터
참고 : https://dojang.io/mod/page/view.php?id=277

문자열을 처리 할 때, 문자열의 길이가 어느정도가 될 지 모르므로 size_t 로 처리해주었다.

찾아보니, strlen 함수도 반환형이 size_t 로 지정되어있었다.


ft_lstdelone

void ft_lstdelone(t_list lst, void (del)(void *))

매개 변수인 lst 를 del함수로 삭제해준다.
이 때, content 는 free 하되, next는 free 해서는 당연히 안된다.


void ft_lstclear(t_list *lst, void (del)(void *))

모든 리스트의 content를 지우고 (del 이용)
free 해준다.


void ft_memmove(void dst, const void *src, size_t len)

처음에 idx = 0으로 주고, 그냥 복사해주었을 때 overlap 문제가 발생했다.

문제가 생긴 소스코드 :

#include <stddef.h>

void	*ft_memmove(void *dst, const void *src, size_t len)
{
	size_t			cnt;
	unsigned char	*str1;
	unsigned char	*str2;

	str1 = (unsigned char *)src;
	str2 = (unsigned char *)dst;
	cnt = 0;
	while (cnt != len)
	{
		str2[cnt] = str1[cnt];
		cnt++;
	}
	return (dst);
}

man memmove에 따르면, 주소의 overlap (중복) 문제도 해결할 수 있어야 하는데, 내가 짠 소스코드는 해당 문제를 해결하지 못하는 memcpy 와 같은 동작을 하고 있었다.

생각한 해결 방법

  • 따로 공간을 내어 복사해서 옮겨주거나,
  • 겹치는 공간부터 복사해준다.

겹치는 공간부터 복사해주는 방법을 선택했다. 공간, 시간적으로 더 효율적인 방법이라고 생각했기 때문이다.

처음에는 apple open source 의 memmove 함수와 비슷한 방법을 생각했다.

/*
 *	memmove.c: memmove compat implementation.
 *
 *	Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
 *
 * See LICENSE for the license.
*/

#include <config.h>
#include <stdlib.h>

void *memmove(void *dest, const void *src, size_t n);

void *memmove(void *dest, const void *src, size_t n)
{
	uint8_t* from = (uint8_t*) src;
	uint8_t* to = (uint8_t*) dest;

	if (from == to || n == 0)
		return dest;
	if (to > from && to-from < (int)n) {
		/* to overlaps with from */
		/*  <from......>         */
		/*         <to........>  */
		/* copy in reverse, to avoid overwriting from */
		int i;
		for(i=n-1; i>=0; i--)
			to[i] = from[i];
		return dest;
	}
	if (from > to && from-to < (int)n) {
		/* to overlaps with from */
		/*        <from......>   */
		/*  <to........>         */
		/* copy forwards, to avoid overwriting from */
		size_t i;
		for(i=0; i<n; i++)
			to[i] = from[i];
		return dest;
	}
	memcpy(dest, src, n);
	return dest;
}

그리고 생각해보니, 굳이 겹치는지 겹치지 않는지 검사하지 않아도 될 것 같았다.
더 단순하게, src의 주소가 dst 보다 앞에 있다면, 뒤부터 복사, 뒤에 있다면 앞부터 복사하면 모든 경우를 다 포함시킬 수 있었다.

#include <stddef.h>

void	*ft_memmove(void *dst, const void *src, size_t len)
{
	long			cnt;
	unsigned char	*str1;
	unsigned char	*str2;

	str1 = (unsigned char *)src;
	str2 = (unsigned char *)dst;
	if (src == dst || len == 0)
		return (dst);
	if (src - dst > 0)
	{
		cnt = -1;
		while (++cnt < (long)len)
			str2[cnt] = str1[cnt];
	}
	else
	{
		cnt = len;
		while (--cnt >= 0)
			str2[cnt] = str1[cnt];
	}
	return (dst);
}

if - else 문을 통해서 해결할 수 있었다!


Makefile

NAME = libft.a
CC = gcc
CFLAGS = -Wall -Wextra -Werror
SRCS = ft_atoi.c ft_bzero.c ft_calloc.c ft_isalnum.c ft_isalpha.c ft_isascii.c \
	ft_isdigit.c ft_isprint.c ft_itoa.c ft_toupper.c \
	ft_memchr.c ft_memcmp.c ft_memcpy.c ft_memmove.c ft_memset.c ft_putchar_fd.c \
	ft_putendl_fd.c ft_putstr_fd.c ft_putnbr_fd.c ft_split.c ft_strchr.c ft_strdup.c \
	ft_striteri.c ft_strjoin.c ft_strlcat.c ft_strlcpy.c ft_strlen.c ft_strmapi.c \
	ft_strncmp.c ft_strnstr.c ft_strrchr.c ft_strtrim.c ft_substr.c ft_tolower.c

BONUS = ft_lstadd_back.c ft_lstadd_front.c ft_lstclear.c ft_lstdelone.c ft_lstiter.c \
	ft_lstlast.c ft_lstmap.c ft_lstnew.c ft_lstsize.c

INCS = libft.h

OBJECTS = $(SRCS:.c=.o)
BONUS_OBJS = $(BONUS:.c=.o)

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

$(NAME) : $(OBJECTS)
	ar rcs $@ $^

all : $(NAME)

clean :
	rm -f $(OBJECTS) $(BONUS_OBJS)

fclean : clean
	rm -f $(NAME)

re : fclean all

bonus : $(OBJECTS) $(BONUS_OBJS)
	ar rcs $(NAME) $^

# if 문 사용.

bonus command 에서 relink 되는 문제점이 발생되었다.

object와, bonus objects 만 받아서, ar rcs 명령어만 실행하므로, 계속해서 링크가 될 수밖에 없었다.

makefile 에서 ifdef 를 사용했다.

ifdef 는 만약 해당 --- 가 정의되어있다면 반드시 true를 반환하는 기능을 한다.

NAME = libft.a
CC = gcc
CFLAGS = -Wall -Wextra -Werror
SRCS = ft_atoi.c ft_bzero.c ft_calloc.c ft_isalnum.c ft_isalpha.c ft_isascii.c \
	ft_isdigit.c ft_isprint.c ft_itoa.c ft_toupper.c \
	ft_memchr.c ft_memcmp.c ft_memcpy.c ft_memmove.c ft_memset.c ft_putchar_fd.c \
	ft_putendl_fd.c ft_putstr_fd.c ft_putnbr_fd.c ft_split.c ft_strchr.c ft_strdup.c \
	ft_striteri.c ft_strjoin.c ft_strlcat.c ft_strlcpy.c ft_strlen.c ft_strmapi.c \
	ft_strncmp.c ft_strnstr.c ft_strrchr.c ft_strtrim.c ft_substr.c ft_tolower.c

BONUS = ft_lstadd_back.c ft_lstadd_front.c ft_lstclear.c ft_lstdelone.c ft_lstiter.c \
	ft_lstlast.c ft_lstmap.c ft_lstnew.c ft_lstsize.c

INCS = libft.h

OBJS = $(SRCS:.c=.o)
BONUS_OBJS = $(BONUS:.c=.o)

ifdef COMMAND_BONUS
	OBJECTS = $(OBJS) $(BONUS_OBJS)
else
	OBJECTS = $(OBJS)
endif

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

$(NAME) : $(OBJECTS)
	@ar rcs $@ $^
	@echo "create libft.a"

all : $(NAME)

clean :
	@rm -f $(OBJECTS) $(BONUS_OBJS)
	@echo "delete object files"

fclean : clean
	rm -f $(NAME)

re : fclean all

bonus :
	@make COMMAND_BONUS=0 all
	@echo "add bonus+"
profile
내가 만들어낸 것들로 세계에 많은 가치를 창출해내고 싶어요.

0개의 댓글