프로토 타입 : int printf(char *format string, ...)
출력문자열의 길이를 return해주는 것을 계속 기억하고 있어야함.
ex) printf("My age is %-3d years old\n", 32);
-형식 문자열(format string): "My age is %-3d years old\n"
-형식 태그(format tag): %-3d
-variadic argument(가변인자) : ...
내 코드의 전체적인 구조는 아래 이미지와 같다.
1. 형식문자열에서 '%'를 기준으로 형식태그와 형식태그가 아닌 부분으로 나뉘어진다.
(가변인자와 형식 문자열에 있는 *의 갯수는 일치한다고 가정한다.(=warning behavior))
2. 형식문자열을 읽으면서, '%'을 기준으로 형식태그가 아닌 부분은 그대로 출력하고, 형식태그인 부분은 ft_strlcpy로 복사하여 이용한다.
3. 형식태그에서 가장 마지막 문자(cspdiuxX%)가 가변인자의 형식을 정할수 있기 때문에, 형식문자의 종류를 담은 배열과 가장 마지막 문자를 비교하면서 일치하는 index값을 얻는다.
index에 대해서 형식문자의 종류를 담은 배열과 종류가 일치하는 함수포인터배열으로 부터 실행되어야하는 함수를 실행시킨다.
이때 함수에 전달되는 인자는 형식태그(char *)와 가변인자목록포인터(va_list *)이다.
4. 형식태그문자열에 쓰인 flag의 내용들을 구조체의 멤버에 담는다. 이로써 더이상 형식태그문자열은 필요하지않고, 구조체의 멤버를 통해 printf 출력이 가능해진다.
5. 형식문자열에서 '*'가 가변인자로 들어오는 정수값과 대응되기 때문에, '*'의 갯수에 따라 가변인자목록포인터의 값을 가져오는 va_arg(ap, sizeof(data_type) 함수의 사용횟수가 달라진다.
따라서 '*'의 갯수에 따라서 함수를 나누어준다.
위 이미지는 %d 서식문자에 대해서 처리하는 함수이다.
flag_decision, flag_decision_more은 형식태그의 flag에 관한 데이터를 구조체에 담는 함수
'*'의 갯수에 따라서 d_flag_nostar, d_flag_onestar, d_flag_twostar로 나뉘어짐
6. '*'의 갯수에 따라 가변인자로 부터 값을 가져온다.
undefined behavior, 귀납적인 사고를 통한 나만의 규칙들을 통해 데이터를 정렬한다.
7. 마지막으로 출력되는 형식을 정밀도, 너비, 문자열의 길이 세가지 기준으로 나누어서 처리한다.
왜냐하면, 예를들어 정밀도가 가장 큰 값(printf("%4.8d, 42);에서
"00000042" 처럼 앞의 부분이 모두 0으로 채워지는 일관된 구조를 가지게된다.
문자열의 길이가 가장 큰 경우는 (printf("%4.5d, 42123442);에서
"42123442" 처럼 정수부분만 출력하면되는 일관된 구조를 가진다.
두 경우 모두 나머지 flag값에 대해서 신경쓰지 않아도 된다는 장점이 있다.
다만, 너비가 가장 큰 경우에는 다소 복잡한 경우가 존재할 수 있다. " 042" , "-00000042" etc
이때는 귀납적 추리과 많은 테스터기를 통해서 차근차근 고쳐나가는 수 밖에 없다.
특이하고 예외적인 부분
1. if (flag->d_dot == 1 && flag->d_precision == 0 && utoa_str[0] == '0')
이러한 경우에는 x,p,d,u 할 것 없이 아무값도 출력해주지 않았다.
2. p에서 NULL 값을 출력하는 것은 undefined behavior이지만, 0x0으로 출력되도록 고정시키기위해서 신경써야했는데, 이때 1번도 만족해야하기때문에 골치 아팠다.
printf("%s", NULL) : 0x0
printf("%.*s", 0, NULL) : 0x
소스코드 :https://github.com/exgs/42cursus/tree/master/04.printf/ft_printf
'42cursus > printf' 카테고리의 다른 글
printf_check-list (0) | 2020.07.10 |
---|---|
printf 자료조사 (0) | 2020.07.06 |