42Seoul

ft_printf.c

Lord DEVader 2022. 12. 21. 13:52
program name libftprintf.a
External functs. malloc, free, wirte, va_start, va_arg, va_copy, va_end
Description 오리지널 printf()를 흉내내는 ft_printf() 구현
prototype ft_printf(const char *, ...);

 

  • Mandatory
    • 오리지널 printf()의 버퍼는 구현하지 않는다.
    • cspdiuxX% 옵션을 handle 한다.
      • %c : character 출력
      • %s : string 출력
      • %p : void * 포인터 출력
      • %d : 10진법 숫자 출력
      • %i : 10진법 정수 출력
      • %u : unsigned 10진법 숫자 출력
      • %x : 16진법 숫자를 소문자로 출력
      • %X : 16진법 숫자를 대문자로 출력
      • %% : '%' 출력
  • 필요 개념
    • va_list
      • : 가변인자. 변수가 몇개 들어올지 모를때 사용할 수 있다.
        • va_list 라는 데이터 타입을 사용.
        • ... 키워드 사용
        • stdarg.h에 정의되어 있다.
    • va_start
      • : va_list를 초기화
      • va_list 변수 ap의 주소 값에다 고정파라미터(가변 파라미터 이외에 최소 1개의 고정 파라미터가 필요) 크기를 더한 위치로 ap를 초기화
    • va_arg
      • : va_list 포인터 변수가 위치한 부분의 데이터를 읽어 반환. 그 후 포인터를 증가 시킴.
      • va_arg(변수, 데이터 타입);
    • va_end
      • : va_list 타입의 변수를 NULL로 초기화.
  • 어려웠던 점. 
    • va_arg를 사용하면, va_list의 변수가 자동으로 다음 주소로 변한다고 알고는 있었지만, 이 변수를 또다른 헬퍼 함수의 파라미터로 쓸때, 움직이질 않았다. 결과적으로, 이 변수의 주소, 즉, 주소의 주소를 넘겨주어야 움직였다.
    • %i는 unsigned int로 받아서 출력을 해주면 되는데, 음수가 없다.
  • Implementation.
    • 프로그램의 일부 핵심 코드입니다.
/*
ft_putp.c

1. 본 함수 ft_putp()에서 0x를 먼저 출력 해준다.
2. printing() 함수에서 16진법에 맞게 주소를 출력해준다.
3. 다시 본함수 ft_putp()에서 출력해준 글자수를 세어주고 반환한다.
*/

static void	printing(size_t n)
{
	if (n < 16)
	{
		ft_putchar_fd("0123456789abcdef"[n % 16], 1);
	}
	else
	{
		printing(n / 16);
		printing(n % 16);
	}
}

int	ft_putp(void *ptr)
{
	size_t	n;
	int		res;

	res = 0;
	n = (size_t)ptr;
	res += ft_putstr_fd("0x", 1); 	//0x 먼저 출력해줌.
	printing(n);					//그 이외 부분 출력.
	while (n != 0)					//출력한 글자수 세어주고 반환.
	{
		n /= 16;
		res++;
	}
	if (ptr == NULL)				//0x0이 출력되기에 3이다. 그런데, 온라인 컴파일러의 경우 5가 나오는 경우도
		res = 3;					//존재한다. 따라서 작업 환경에 따라 주의를 요함.
	return (res);
}
//receive address.
//print hexa address
/*
ft_printf.c

1. va_list 생성, 초기화.
2. check_str()에서 인자로 받은 문자열을 출력해주는데, 만약 %를 만나면, check_option()호출
3. check_option()에서는 옵션의 종류에 따라 적절한 함수를 실행 해준다.
4. 본 함수로 돌아와, va_list를 종료해주며, 출력한 글자수를 반환한다.
*/

#include "ft_printf.h"

static int	check_option(va_list *ap, char c)
{
	int	res;

	res = 0;
	if (!(c == '%' || c == 'u' || c == 'p' || c == 'c' || c == 's' || \
	c == 'd' || c == 'i' || c == 'x' || c == 'X'))
		return (-1);
	else if (c == '%')
		res += ft_putchar_fd('%', 1);
	else if (c == 'c')
		res += ft_putchar_fd(va_arg(*ap, int), 1);
	else if (c == 's')
		res += ft_putstr_fd(va_arg(*ap, char *), 1);
	else if (c == 'd' || c == 'i')
		res += ft_putnbr_base_fd(va_arg(*ap, int), 1, c);
	else if (c == 'u')
		res += ft_putunbr_fd(va_arg(*ap, unsigned int), 1);
	else if (c == 'x' || c == 'X')
		res += ft_puthexnbr_fd(va_arg(*ap, long long), 1, c);
	else if (c == 'p')
		res += ft_putp(va_arg(*ap, void *));
	return (res);
}
// 옵션을 체크하고, 그에 맞게 출력하는 함수를 불러오는 함수.

static int	check_str(va_list *ap, char *format)
{
	int	res;

	res = 0;
	while (*format)
	{
		if (*format == '%')
		{
			format++;
			if (*format == '\0')
				break ;
			if (*format == '%' || *format == 'p' || *format == 'u' \
			|| *format == 'c' || *format == 's' || *format == 'd' \
			|| *format == 'i' || *format == 'x' || *format == 'X')
				res += check_option(ap, *format);
			else
			{
				res += ft_putchar_fd('%', 1);
				res += ft_putchar_fd(*format, 1);
			}
		}
		else
			res += ft_putchar_fd(*format, 1);
		format++;
	}
	return (res);
}

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

	res = 0;
	va_start(ap, format);					//va_list의 변수 ap를 고정인자 format의 다음으로 위치시킨다. 즉, 초기화 작업.
	res = check_str(&ap, (char *)format); 	//ap의 주소를 넣어줘야 함수간 이동하며 사용에도 지장이 없다.
	va_end(ap);	//가변인자 사용후 마무리.
	return (res);
}
//va_list로 가변 인자를 받아 초기화후, check_str함수로 출력을 진행해준다.
/*
ft_puthexnbr_fd.c

1. check_nbr()에서 인자가 음수인 경우에 대해 처리를 해주며, 진법을 확정지어, 주소로 주어진 정수에 저장한다.
2. printing()에서 x,X 상황에 맞게 출력을 진행해준다.
3. 본 함수인 ft_puthexnbr()로 돌아와, 출력한 글자의 갯수를 세어주고 반환한다.
*/



#include "ft_printf.h"

static void	printing(unsigned int num, char base)
{
	if (num >= 16)
	{
		printing(num / 16, base);
		printing(num % 16, base);
	}
	else
	{
		if (num >= 10 && base == 'X')
			ft_putchar_fd('A' + num - 10, 1);
		else if (num >= 10 && base == 'x')
			ft_putchar_fd('a' + num - 10, 1);
		else
			ft_putchar_fd('0' + num, 1);
	}
}

int	ft_put_hex_nbr(unsigned int num, char base)
{
	int	res;

	printing(num, base);
	res = 0;
	while (num != 0)
	{
		num /= 16;
		res++;
	}
	if (res == 0)
		res = 1;
	return (res);
}

`

  • 실행 결과