ft_printf

slee2·2022년 5월 23일
0

42Seoul

목록 보기
3/15
post-thumbnail

Reference

프로젝트 이름만 봐도 짐작이 가듯이,
이 프로젝트는 printf 메서드를 구현하는 프로젝트이다.
우리가 흔히 쓰는 pinrtf를 매우 편리하게 사용할 수 있던 이유는 그만큼 printf에 나름 섬세한 기능들이 있기 때문이다.

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

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

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

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

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

%pvoid * 형식의 포인터 인자를 16진수로 출력한다.

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

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

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

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

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

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


가변인자

va_start, va_list, va_arg 와 같은 메서드는
int ft_printf(const char *format, ...) 로 인해 만들어진 가변인자를 구분하는데 사용한다.

우리가 알고 있는 printf의 경우에도
printf("a - %d\n", 3)
printf("%d %d\n", 3, 4)
와 같이 인자가 몇개 들어올지 모르므로, ...를 이용하여 가변인자를 설정하게 된다.

그럼 어떻게 사용하는지 알아보자.

int	ft_printf(const char *format, ...)
{
	va_list		ap;

	va_start(ap, format);
	int a = va_arg(ap, int);
    int b = va_arg(ap, int);
	va_end(ap);
	return (0);
}

설명을 위해 간단하게만 적었다. 위 코드를 기반으로 알아보자.

먼저, ft_printf(10, 20, 30, 40) 으로 예를 들어보겠다.

va_list : va 메서드를 사용하기 위해 va_list 타입을 선언해줘야한다.

va_start(ap, format)

va_start를 실행하게 되면 format 개수만큼 가변인자를 가져와 va_list가 만들어지며 첫 부분에 ap가 설정된다.

va_arg(ap, int)

va_arg를 실행하게 되면 va_list에서 두 번째 매개변수(int) 타입으로 값을 하나 꺼낸 뒤 a = 10이 되고, ap가 한칸 이동하게 된다.

이후에 va_arg(ap, int) 가 한 번 더 실행하게 되므로,

b = 20 이 되고 ap가 한칸 이동하게 된다.

va_end(ap)
va_listNULL로 초기화한다.


활용

int	ft_printf(const char *format, ...)
{
	int			pos[1];
	va_list		ap;
	int			cnt;
	int			cnt_d;

	va_start(ap, format);
	pos[0] = 0;
	cnt = 0;
	cnt_d = 0;
	while (format[pos[0]])
	{
		if (format[pos[0]] == '%')
		{
			cnt_d = specifier(format, ap, pos);
			if (cnt_d == -2)
				return (0);
			cnt += cnt_d;
		}
		else
			write(1, &format[pos[0]], 1);
		pos[0]++;
		cnt++;
	}
	va_end(ap);
	return (cnt);
}

결국에 다음 인자를 확인하는 작업은 %가 시작한 뒤에 작업을 시작하므로 위와 같이 시작하게 된다.

va_arg의 경우

char	*ft_k_type(t_options *opt, va_list ap)
{
	if (opt->type == 'd' || opt->type == 'i')
		return (ft_itoa(va_arg(ap, int)));
	else if (opt->type == 'u')
		return (ft_u_display(va_arg(ap, unsigned int)));
	else if (opt->type == 'x')
		return (ft_16str('x', va_arg(ap, unsigned int)));
	else if (opt->type == 'X')
		return (ft_16str('X', va_arg(ap, unsigned int)));
	else if ((opt->type == 'p' && opt->dot == -1)
		|| (opt->type == 'p' && opt->dot != -1 && opt->width != 0))
		return (ft_strjoin
			(ft_strdup("0x"), ft_p16(va_arg(ap, unsigned long long))));
	else if (opt->type == 's')
		return (ft_strdup(va_arg(ap, char *)));
	else if (opt->type == 'c')
		return (ft_str_c_dup(va_arg(ap, int)));
	else if (opt->type == '%')
		return (ft_strdup("%"));
	return (0);
}

이런 식으로 어떤 서식지인지에 따라 타입을 다르게 받을수가 있다.
이를 이용해서 각각 다른 처리를 하면 쉽게 해결할 수 있다.

그 외에 여러 케이스에 대한 섬세한 처리가 중요하다. 늘 그렇듯이 특수한 경우는 항상 존재하며, 이러한 케이스를 얼마나 잘 막느냐가 이 프로젝트의 통과 유무를 결정할 수 있다.

0개의 댓글