📚 /42cursus

1. 소개


putnbr and putstr aren’t enough

42서울 본과정 입과 후 두번째로 수행한 과제로, 말 그대로 C언어의 printf 함수를 재현하는 과제이다. bonus는 수행하지 않았고, 기본적인 내용만 구현하다보니 생각보다 난이도가 쉬웠던 것 같다. libc의 printf 함수를 재구현해야 하며, 실제 printf처럼 버퍼 관리를 수행해서는 안 된다. 서식 지정자 cspdiuxX%를 구현해야한다.



2. ft_printf 명세서




3. 개념 정리


3-1. 가변인자

가변인자 다루는 것에 중점을 둔 과제인 만큼, va_ 함수를 통해 가변인자를 통제하는 방법을 알아야한다.


3-2. 바이트 패딩

우리는 왜 char형을 읽는 순간 (char)va_arg(ap, int); 을 사용하는가? char형 자료를 읽는데 왜 int형 만큼 읽어오는 것인가? 바이트 패딩은 CPU가 데이터를 효율적으로 처리하게 하는 데 중요한 역할을 하며, 당연하게도 이는 32비트와 64비트 운영 체제 간의 데이터 처리 방식에 차이를 가져온다.

그러나 바이트 패딩만으로 설명할 수 있을까? 다만 바이트 패딩만으로 위 질문에 답하기 애매한것은 운영체제의 비트에 따라 데이터 처리 단위가 상이하기 때문이다.

32비트 운영 체제는 4바이트, 64비트 운영 체제는 8바이트 단위로 데이터를 처리하기 때문에, 카뎃의 의도가 정말 바이트 패딩을 통해 데이터를 효율적으로 처리하기 위함이었다면 운영체제의 종류를 고민한 흔적이 코드에 들어있어야 할 것 이다.

그렇지만 대부분 (char)va_arg(ap, int); 을 명시적으로 사용하곤 한다.

유력한 다른 이유는 원시 C언어 시절에는 함수 프로토타입이 존재하지 않았기 때문에, 함수로 전달되는 인자의 정확한 자료형을 알 수 없었다. 따라서 모든 인자는 int로 처리되었고, 이러한 관행의 흔적이라고 보는 것이 좀 더 타당해보인다.


4. Mandatory



step 01

ft_isformat을 통해 지정된 포맷인지 확인하며 문자열을 읽어나간다.

step 02

지정된 포맷을 만났을 경우, ft_parse_format 함수를 통해 가변인자를 읽어 각 포맷에 맞게 변환한다.

step 03

지정된 포맷이 아닌 경우, 문자 하나 하나 write 함수를 통해 출력한다.


4-1. 구현

ft_printf

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

	va_start(ap, format);
	print_cnt = ft_print_format(format, ap);
	va_end(ap);
	return (print_cnt);
}

우선 printf의 return 값은 print된 문자의 수이므로 printf_cnt 함수에 담았다가 return 하는 방식을 선택했다.


ft_printf_format

int	ft_print_format(const char *str, va_list ap)
{
	int	print_cnt;

	print_cnt = 0;
	while (*str)
	{
		if (*str == '%' && ft_isformat(*(str + 1)))
		{
			ft_parse_format(*(str + 1), ap, &print_cnt);
			str++;
		}
		else
			ft_putchar_fd(1, *str, &print_cnt);
		str++;
	}
	return (print_cnt);
}

함수 ft_printf_format은 인자로 받은 const char *fortmat을 문자 %가 나올 때까지 탐색한다. 문자 %을 발견하면, % 바로 뒤의 포맷을 확인해서 해당하는 포맷과 알맞은 출력을 진행한다.


ft_putbase

void ft_putbase(unsigned int n, char *base, unsigned int number, int *cnt)
{
    if (*cnt == -1)
        return ;
    if (n / number != 0)
        ft_putbase(n / number, base, number, cnt);
    ft_putchar_fd(1, base[n % number], cnt);
}

진법에 맞는 숫자로 출력을 원할 때, 재귀를 사용하면 생각보다 코드가 깔끔해진다.



5. Reference