프로젝트 이름만 봐도 짐작이 가듯이,
이 프로젝트는 printf
메서드를 구현하는 프로젝트이다.
우리가 흔히 쓰는 pinrtf
를 매우 편리하게 사용할 수 있던 이유는 그만큼 printf
에 나름 섬세한 기능들이 있기 때문이다.
int ft_printf(const char *format, ...);
다음 서식 지정자를 구현하라 : cspdiuxX%
필요한 서식 지정자에 대한 간단한 설명이다:
%c
는 단일 문자 (character) 한 개를 출력한다.
%s
는 문자열 (string) 을 출력한다.
%p
는 void *
형식의 포인터 인자를 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_list
를 NULL
로 초기화한다.
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);
}
이런 식으로 어떤 서식지인지에 따라 타입을 다르게 받을수가 있다.
이를 이용해서 각각 다른 처리를 하면 쉽게 해결할 수 있다.
그 외에 여러 케이스에 대한 섬세한 처리가 중요하다. 늘 그렇듯이 특수한 경우는 항상 존재하며, 이러한 케이스를 얼마나 잘 막느냐가 이 프로젝트의 통과 유무를 결정할 수 있다.