100%를 한번에 바꾸는건 어려워도 1%를 100번 바꾸는건 쉽다.

생각정리 자세히보기

42 Seoul

[42 Seoul] Printf

dc-choi 2022. 4. 22. 11:43
반응형

printf

putnbr와 putstr으로는 만족할 수 없기 때문에...

 

요약 : 이 프로젝트는 꽤 단순합니다. 여러분은 printf 함수를 직접 구현하시면 됩니다. 희망컨대 여러분들은 cheater로 지목될 수 있다는 두려움 없이 추후 프로젝트에서 이것을 재활용할 수 있습니다.

 

여러분은 주로 가변 인자 (variadic arguments) 를 사용하는 방법에 대해 배울 것입니다.

 

C에서 printf 함수의 다재다능함은 프로그래밍에 있어 우리에게 훌륭한 연습이 됩니다. 이 프로젝트는 중간 정도의 난이도를 가지며, 여러분들이 C에서 가변 함수들을 배울 수 있도록 도와줍니다.

 

성공적인 ft_printf의 핵심은 체계적이고 확장성 있는 코드입니다.

 

알아야할 선수지식

Libft 사용

Libft를 사용하기 위해서는 make -C를 이용해서 Libft를 먼저 컴파일한 후 사용한다.

-C: make를 실행하지않고 일단 해당 위치로 이동후, make 실행

 

Makefile 예제는 다음과 같다.

NAME = libftprintf.a

CC = gcc
CFLAGS = -Wall -Wextra -Werror
AR = ar rcs
RM = rm -f

FILES = ft_printf \
		ft_printf_type \
		ft_printf_utils \

LIBFT_DIR = ./Libft/

SRCS_DIR = ./
SRCS = $(addprefix $(SRCS_DIR), $(addsuffix .c, $(FILES)))

OBJS_DIR = ./
OBJS = $(addprefix $(OBJS_DIR), $(addsuffix .o, $(FILES)))

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

$(NAME): $(OBJS)
	make all -C $(LIBFT_DIR)
	cp Libft/libft.a $(NAME)
	$(AR) $@ $^

all: $(NAME)

clean:
	$(RM) $(OBJS)
	make fclean -C $(LIBFT_DIR)

fclean: clean
	$(RM) $(NAME)
	make fclean -C $(LIBFT_DIR)

re: fclean all

.PHONY: all clean fclean re

 

가변인자

인자의 개수가 정해지지 않을 때, 임의의 인자를 처리하는 것입니다. 먼저 가변 인자 함수를 만들기 위해서는 stdarg.h 헤더파일을 포함해야합니다. 이 헤더 파일에 가변 인자 함수를 만들 때 필요한 각종 매크로들이 정의되어 있습니다.

 

가변 인자를 포함한 함수 = 필수 인자 + 선택적 인자

 

Va_list

가변 인자의 목록으로, 가변 인자의 시작 주소를 가리킬 포인터입니다. 모양은 멋있게 생겼지만 내부적으로는 char * 로 정의되어 있는 녀석입니다.

 

va_start(list, args)

Va_list 타입의 list를 초기화 해주는 매크로입니다. va_start()는 마지막 필수 인자를 매개 변수로 넣기만 하면 자동으로 가변 인자의 시작 주소를 계산하여 list에 할당해주는 역할을 수행합니다.

 

va_arg(list, Data Type)

va_arg()는 Va_list가 참조하고 있는 특정 가변 인자를 역참조하고, Va_list 내의 다음 가변 인자를 참조하도록 해주는 매크로입니다. 다음 가변 인자를 참조하도록 만들 때는 sizeof(Data Type)만큼 다음에 있는 값을 참조하도록 만듭니다.

char, short 의 경우에는 int로 대신 쓰고, float의 경우에는 double로 대신 쓴 이후 형 변환을 해주어야 합니다.

 

 va_end(list)

가변 인자 목록을 지칭하는 Va_list 타입의 포인터 변수를 NULL을 할당하면서 가변 인자 사용의 끝낼 때 사용합니다. Va_list 타입으로 형 변환 후 0을 넣어줍니다. 여기서의 0은 NULL을 의미하는데, va_arg()를 사용하여 특정 메모리 공간을 참조하고 있는 포인터에 NULL을 할당하여 참조하는 공간이 없도록 만듭니다.

 

Common Instructions

프로젝트는 Norm 규칙에 맞춰 작성되어야 합니다. 보너스 파일/함수가 존재할 경우, 그 또한 norm 검사에 포함되며 norm error가 있을 시 0점을 받게 됩니다.

 

함수들은 정의되지 않은 행동들과 별개로 예기치 않게 중단되어서는 안 됩니다.(예를 들어, segmentation fault, bus error, double free 등) 만약 이렇게 중단되면, 당신의 프로젝트는 작동하지 않는 것으로 여겨지고 평가에서 0점을 받을 것입니다.

 

과제에서 필요한 경우, -Wall -Wextra -Werror 플래그를 지정하여 컴파일을 수행하는 Makefile을 제출해야 합니다. Makefile은 relink 되어서는 안 됩니다.

 

Makefile은 최소한 $(NAME), all, clean, fclean, re 규칙을 포함해야 합니다.

 

프로젝트에 보너스를 제출하려면, Makefile에 보너스 규칙을 포함해야 합니다. 이 보너스 규칙은 프로젝트의 메인 파트에서 금지되었던 모든 다양한 헤더, 라이브러리, 또는 함수들을 추가하여야 합니다. 보너스 과제는 반드시 _bonus.{c/h}라는 별도의 파일에 있어야 합니다. 반드시 수행하여야 하는 메인 파트의 평가와 보너스 파트의 평가는 별도로 이뤄집니다.

 

만일 프로젝트에서 여러분의 libft 사용을 허용한다면, 소스들과 관련 Makefile을 함께 루트 폴더 안에 있는 libft 폴더에 복사해야 합니다. 프로젝트의 Makefile은 우선 libft의 Makefile을 사용하여 라이브러리를 컴파일한 다음, 프로젝트를 컴파일해야 합니다.

 

이 과제물을 제출할 필요가 없고, 채점 받을 필요가 없을지라도, 저희는 여러분들이 프로젝트를 위한 테스트 프로그램을 만들 것을 권장합니다. 이것은 여러분의 과제물과 동료들의 과제물을 쉽게 테스트할 수 있게 도울 것입니다. 또한, 평가를 진행할 때 이러한 테스트 프로그램들이 특히 유용하다는 사실을 알게 될 것입니다. 평가 시에는 여러분의 테스트 프로그램과 평가 받는 동료의 테스트 프로그램들을 당연히 자유롭게 사용할 수 있습니다.

 

할당된 git 저장소에 과제물을 제출하세요. 오직 git 저장소에 있는 과제물만 등급이 매겨질 것입니다. Deepthought가 평가하는 과제의 경우엔, 동료평가 이후에 Deepthought가 수행됩니다. 만약 Deepthought 평가 중에 오류가 발생한다면, 그 즉시 평가는 중지될 것입니다.

 

Mandatory part

프로그램 이름 libftprintf.a
제출할 파일 *.c, */*.c, *.h, */*.h, Makefile
Makefile 규칙 all, clean, fclean, re, bonus
사용가능한
외부 함수
malloc, free, write, va_start, va_arg, va_copy, va_end
직접 만든 libft 사용 가능
설명 실제 printf의 동작을 모방한 ft_printf를 포함하는 라이브러리를 작성하세요.

 

ft_printf의 프로토타입은 int ft_printf(const char *, ...); 이어야 합니다.

 

여러분은 libc의 printf 함수를 재구현해야 합니다.

 

실제 printf처럼 버퍼 관리를 수행해서는 안 됩니다.

 

다음 서식 지정자를 구현하세요 : cspdiuxX%

 

실제 printf와 비교하여 채점할 것입니다.

 

ar 명령어를 이용하여 라이브러리를 만들어야 합니다. libtool을 사용하는 것은 금지됩니다.

 

필요한 서식 지정자에 대한 간단한 설명입니다:

 

%c는 단일 문자 (character) 한 개를 출력합니다.

 

%s는 문자열 (string) 을 출력합니다.

 

%p는 void * 형식의 포인터 인자를 16진수로 출력합니다.

 

%d는 10진수 숫자를 출력합니다.

 

%i는 10진수 정수를 출력합니다.

 

%u는 부호 없는 10진수 숫자를 출력합니다.

 

%x는 소문자를 사용하여 숫자를 16진수로 출력합니다.

 

%X는 대문자를 사용하여 숫자를 16진수로 출력합니다.

 

%%는 퍼센트 기호 (%) 를 출력합니다.

 

더 완벽한 참고 자료는 man 3 printf / man 3 stdarg

 

Bonus part

필수 구현 파트가 완벽하지 않으면, 보너스는 생각도 하지 마세요.

 

모든 보너스를 구현할 필요는 없습니다.

 

다음 플래그들의 조합 (any combination) 을 구현하세요 : '-0.', 그리고 각 서식 지정자별 최소 폭

 

다음 플래그를 모두 구현하세요 : '# +' (맞아요, 한 개는 공백이에요)

 

보너스를 구현하실 예정이라면, 단순하게 접근하지 않기 위하여 처음부터 어떻게 구현을 해야 할 지 고민해 보셔야 할 겁니다.

 

구현 설명

int ft_printf(const char *format, ...)

int	ft_printf(const char *format, ...)
{
	int			len;
	int			idx;
	va_list		list;

	va_start(list, format);
	len = 0;
	idx = 0;
	while (format[idx])
	{
		if (format[idx] == '%')
		{
			len += check_type(format[idx + 1], &list);
			idx++;
		}
		else
			len += ft_printf_char(format[idx]);
		idx++;
	}
	va_end(list);
	return (len);
}

 

int check_type(const char c, va_list *list)

int	check_type(const char c, va_list *list)
{
	if (c == 'c')
		return (ft_printf_char(va_arg(*list, int)));
	else if (c == 's')
		return (ft_printf_str(va_arg(*list, char *)));
	else if (c == 'd' || c == 'i')
		return (ft_printf_nbr(va_arg(*list, int)));
	else if (c == 'u')
		return (ft_printf_unsigned(va_arg(*list, unsigned int)));
	else if (c == 'x' || c == 'X')
		return (ft_printf_hex(va_arg(*list, unsigned int), c));
	else if (c == 'p')
		return (ft_printf_ptr(va_arg(*list, unsigned long long)));
	else if (c == '%')
		return (ft_printf_char('%'));
	return (0);
}

 

int ft_printf_char(int c)

int	ft_printf_char(int c)
{
	int	len;

	len = ft_putchar_fd(c, 1);
	return (len);
}

 

int ft_printf_str(char *str)

int	ft_printf_str(char *str)
{
	int	len;

	if (str == 0)
	{
		len = ft_putstr_fd("(null)", 1);
		return (len);
	}
	len = ft_putstr_fd(str, 1);
	return (len);
}

 

int ft_printf_nbr(int nbr)

int	ft_printf_nbr(int nbr)
{
	int		len;
	char	*str;

	str = ft_itoa(nbr);
	len = ft_putstr_fd(str, 1);
	free(str);
	return (len);
}

 

int ft_printf_unsigned(unsigned int nbr)

int	ft_printf_unsigned(unsigned int nbr)
{
	size_t	len;

	len = ft_putnbr_base(nbr, "0123456789");
	return (len);
}

 

int ft_printf_hex(unsigned int nbr, const char type)

int	ft_printf_hex(unsigned int nbr, const char type)
{
	size_t	len;

	len = 0;
	if (type == 'X')
		len = ft_putnbr_base(nbr, "0123456789ABCDEF");
	else
		len = ft_putnbr_base(nbr, "0123456789abcdef");
	return (len);
}

 

int ft_printf_ptr(unsigned long long ptr)

static void	ft_putptr(unsigned long long ptr, size_t *len)
{
	if (ptr >= 16)
	{
		ft_putptr(ptr / 16, len);
		ft_putptr(ptr % 16, len);
	}
	else
	{
		if (ptr < 10)
			*len += ft_putchar_fd('0' + ptr, 1);
		else
			*len += ft_putchar_fd(ptr - 10 + 'a', 1);
	}
}

int	ft_printf_ptr(unsigned long long ptr)
{
	size_t	len;

	len = 0;
	len += ft_putstr_fd("0x", 1);
	ft_putptr(ptr, &len);
	return (len);
}
반응형

'42 Seoul' 카테고리의 다른 글

[42 Seoul] Born2BeRoot 설정가이드(Debian)  (0) 2022.05.13
[42 Seoul] Born2BeRoot 설치가이드(Debian)  (0) 2022.05.12
[42 Seoul] Born2beRoot  (0) 2022.05.09
[42 Seoul] Get Next Line  (0) 2022.03.07
[42 Seoul] Libft  (0) 2022.01.07