ft_printf 정리

김호·2021년 11월 1일
0

ft_printf 과제는 7월에 문제가 수정되었다

printf

<stdio.h> 헤더파일 안에 있으며

int printf(const char *restrict format, ...)로 정의된다.

출저 : https://linux.die.net/man/3/printf

첫 번째 매개 변수로 문자열(format)이 입력되고, 그 이후는 가변 인자로 n개의 매개 변수를 받는다.

반환값이 int형으로 정의된 이유는 출력된 문자열의 수가 반환됨

format%[parameter][flags][width][.precision][length] type 형식을 가지며 해석하면

%[n][flag][출력 너비][출력되는 값(숫자)의 정확도][데이터 타입 범위] type(서식문자)이다.

flag

  • [#] 진법 형식에 맞게 0, 0x 0X를 추가
  • [0] width에 맞게 주어진 너비의 빈자리를 0으로 채움
  • [ ] 양수일 땐 +대신 공백, 음수일 땐 -출력
  • ['] 정수와 지수에 천 단위를 표시함 (2,000)
  • [+] 양수일 때 +, 음수일 때 - 표시
  • [-] 좌측 정렬

width

  • [n] n은 양수이며, 출력될 길이
  • [*] 너비 값을 인자로 받음

precision

.만 사용된 경우

  • [cp] 정확도를 무시
  • [diouxX] flag필드로 0이 주어지면, 0옵션을 무시함
  • [feEgGaA] 소수점 아래를 출력하지 않고, 마지막 숫자는 반올림
  • [s] 문자열 출력하지 않음
  • [*s] 출력할 최대 길이를 인자로 받음

.n이 입력된 경우 앞의 width를 무시하고 n만큼 출력됨

  • [c] 정확도를 무시
  • [diouxX] 출력할 최대 자릿수를 지정, 출력값이 적은경우 남은 자릿수는 0으로 채움
  • [feEgGaA] 출력할 소수점 자릿수를 지정, 출력할 자리 직후의 숫자를 반올림
  • [s] 출력할 최대 문자열 길이를 지정
  • [p] 0x를 제외하고 총 길이로 주어진 n에 맞춰 출력

length

  • [signed char] - hhd, hhi
  • [unsigned char] - hhu, hhx, hhX
  • [short] - hd, hi
  • [unsigned short] - hu, hx, hX
  • [long] - ld, li
  • [long long] - lld, lli
  • [unsigned long] - lu, lx, lX
  • [unsigned long long] - llu, llx, llX
  • [wint_t] - lc
  • [wchar_t *] - ls

type

문제 정리글에 적음

매개 변수 수의 조건

printf함수의 매개 변수는 0 ~ n개의 값을 가질 수 있으며, format의 형식 지정자값에 따라 매개 변수의 수가 다르다.

예를들어

printf("%d %d %d", 1, 2, 3, 4);
`

인 경우, 형식 지정자가 3개 이므로 매개 변수는 최소 3개 이상이 와야한다. 형식 지정자보다 매개 변수가 많을 경우 그 이후는 무시된다.

그러므로 형식 지정자 <= 매개 변수 조건이 맞춰줘야 한다.

가변 인자

가변 인자에 사용할 함수는 <stdarg.h> 헤더 파일 안에 있다.

va_list는 가변 인자의 메모리 주소를 저장할 포인터이다.

  • void va_start(va_list ap, 첫 번째 매개변수) - 두 번째 인자로 첫 번째 매개변수를 넣어, ap(가변 인자 포인터변수)가 이후 들어올 가변 인자를 가르키게 한다.
  • type va_arg(va_list ap, type) - ap(가변 인자 포인터변수)가 가르키는 곳에서 type자료형 만큼 데이터를 읽어와 type자료형으로 반환
  • void va_end(va_list ap) - ap가 가르키는곳을 NULL로 초기화
    출저 : https://linux.die.net/man/3/stdarg
#include <stdio.h>
#include <stdarg.h>

void printNumbers(int args, ...) 
{
    va_list ap; // 가변 인자의 주소를 저장할 포인터 선언

    va_start(ap, args); // 가변 인자 변수가 args이후에 들어올 변수를 가르키게 한다
    for (int i = 0; i < args; i++)
    {
        int num = va_arg(ap, int); // 가변 인자 변수에서 int자료형 크기만큼 데이터를 읽고 int형으로 반환
        printf("%d ", num);          
    }
    va_end(ap); // 가변 인자 변수를 NULL로 초기화
}

int main()
{
    printNumbers(4, 10, 20, 30, 40);    // 인수 개수 4개
    return 0;
}


그림으로 표현하면 va_start(ap, args) 한 순간 ap는 args이후 매개 변수를 가르키고 있다.

코드, 그림 출저 : https://dojang.io/mod/page/view.php?id=577

char 자료형과 int 자료형

int num = va_arg(ap, int);
char ch = va_arg(ap, int);

%d, %c같이 int나 char형의 값을 읽어올 때 동일하게 int형을 사용했다.

int형은 4byte char형은 1byte인데 왜 똑같이 사용하는 걸까?

평가 진행 중, 한 평가자가 이런 질문을 했는데 대답하질 못했다.

그 평가자분이 답을 알려주셨는데 va_list 자료형이 내부적으로 가르키는 값은 4byte씩 나뉘어져 있어서 char, int 자료형을 동일하게 사용한다는 것이다.

지금와서 생각해보면 va_list 자료형은 포인터 변수이고, 포인터 변수는 4byte이기 때문에

가변 인자들이 4byte씩 나뉘어져있다고 생각한다.

ft_printf

bonus part는 못했고 mandatory part만 했다.

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

	result = 0;
	va_start(ap, format);
	result = parse_format(ap, (char *)format);
	va_end(ap);
	return (result);
}

va_list 가변 인자 포인터 변수를 ap라는 이름으로 선언하고, va_start(ap, format)를 이용해 가변 인자를 가르키게 했다.

이제 ap(가변 인자를 가르킴), format을 parse_format함수를 이용해 문자열을 파싱하며 출력하고 출력된 문자열의 수를 반환하게 했다.

int	parse_format(va_list ap, char *format)
{
	int		result;

	result = 0;
	while (*format)
	{
		if (*format == '%')
		{
			format++;
			if (*format == 0)
				break ;
			/*
            		서식 문자에 따라 맞는 값을 출력한다.
            		*/
		}
		else
			result += ft_putchar(*format); // 일반 문자를 출력
		format++;
	}
	return (result);
}

ft_printf("test%d", 123) "test%d" 라는 문자열이 들어오면 처음부터 문자열의 끝까지 순회하며

일반 문자의 경우 바로 출력하고, %(서식문자)인 경우 다음 문자를 확인에 해당하는 값을 가변인자에서 읽어와 출력한다.

if (*format == 0)
	break ;

이 코드를 넣은 이유는 ft_printf("test %")와 같이 맨 끝에 %만 있을 경우 다음 문자를 확인할 때 NULL을 검사하기 때문에 문제가 생길 수 있어 NULL일 경우 바로 빠져 나오게 했다.

사용한 테스터

https://github.com/paulo-santana/ft_printf_tester
https://github.com/chronikum/printf42_mandatorytester
https://github.com/Tripouille/printfTester

0개의 댓글