Search
Duplicate

Solution

1. 필수개념

printf 란?

처음 c언어를 배울때 출력방법중 하나로 알게되는 그 printf 맞다
일반 문자열에 %들을 통해서 문자열을 구성하여 출력한다.
int printf(const char *format, ...);
C++
복사
인자
출력할 문자열
%에 해당되는 변수들
반환값
출력된 문자열 갯수
예시
int num = 5; ret = printf("Num:%d\n", num);
C++
복사
실제 출력 : Num:5$ ($는 엔터)
% 뒤에 붙어있는 d 같은 애들을 서식지정자라고 부른다.
이떄 ret 의 값은 6 이다. (엔터 포함)
서식지정자
서식지정자에는 아래와 같은 규칙들이 존재한다.
%[flags][width][.precision][length]type
C++
복사
아래 로직 부분에서 하나씩 알아보면서 직접 구현해본다.
[ ] 는 옵션이고 %type 은 필수로 들어가야한다.

가변인자

위의 프로토타입을 보면 두번째 인자가 ... 인것을 알수있다.
앞의 문자열에 있는 형식의 갯수에 따라서 인자의 갯수가 달라지기 때문에 가변인자를 쓴다.
자세한 설명과 사용방법은 여기를 참고하자

2. 테스트

3. 로직

flag, width, precision 규칙은 이미 다른분들이 잘 정리했기 때문에 생략하고,
규칙에 맞게 잘 구현하는 방법을 적었습니다.
기본로직은 파싱 → 포장 → 출력 으로 이루어져 있습니다.
% 가 나올떄까지는 그대로 출력하고
% 가 나오기 시작한 부분부터 아래의 로직을 구현하면 됩니다.
구체적인 코드는 여기를 확인해주세요.

3-1. 파싱

% 가 나오면 파싱을 시작합니다.
다음와같이 flag, width, precison, type 을 파싱해야합니다.
%[flags][width][.precision]type
C++
복사
파싱한 결과값을 저장할 구조체를 선언해줍니다.
typedef struct s_flag { int flag; int width; int precision; int type; } t_flag;
C++
복사
서식 지정자는 순서대로 들어옵니다.
위의 형식을 지키지 않은 입력은 오류로 냅둡니다.
따라서 구현 해야 할 파트는 총 4개 입니다.

flag 파싱

- 또는 0 이 올 수 있습니다. 둘다 올 수 있습니다.
같은 문자가 여러개가 들어올수있습니다.
최종 값

width 파싱

0 으로 초기화해줍니다.
숫자가 오거나, * 이 올수있습니다.
숫자 인 경우 atoi 하면 됩니다.
* 인 경우 va_arg(ap, int) 로 값을 받아옵니다.
이떄 값이 음수인경우
flag 를 - 로,
width 에는 절대값을 씌워 양수로 바꿉니다.
최종 값

precision 파싱

-1 로 초기화 해줍니다.
.여러개 올 수 있습니다.
0 으로 초기화 해줍니다.
숫자가 오거나, * 이 올수있습니다.
숫자 인 경우 atoi 하면 됩니다.
* 인 경우 va_arg(ap, int) 로 값을 받아옵니다.
이떄 값이 음수인경우
-1 로 초기화합니다.
최종 값

type 파싱

%cspdiuxX 중에 하나 올수있습니다.
만약에 없으면 오류이므로 다음 단계로 넘어가지 않습니다.
최종 값
주의! type 이 없는 경우 segfault 가 뜰수있습니다.

3-2. va_arg

va_arg 를 통하여 값을 읽습니다.
Search
va_arg 코드의 사본
type
코드
크기
va_arg(ap, unsigned int)
4바이트
0 ~ 42억
va_arg(ap, void *)
8바이트
0 ~ 엄청 큰 값
COUNT3
long long 으로 캐스팅해서 변수로 저장하고 다니면 편합니다.
long long 이 8바이트 이기때문에 오버플로우되든 언더플로우되든 일단 저장해놓고
다시 값을 써야할때 해당 타입으로 캐스팅해서 쓰면 됩니다.
예)
char *str = "test"; long long value; value = (long long)str; printf("%s", (char *)value); //정상 출력
C++
복사

3-3. 포장

최종 출력 형식은 다음과 같습니다.
[left_margin][sign][prefix][zero][value][right_margin]
C++
복사
위의 형식을 저장하기 위한 구조체를 선언합니다.
typedef struct s_box { int left_margin; //왼쪽 스페이스 개수 char sign; // - 출력 int zero; //출력할 0의 개수 int prefix; //0x 출력 여부 (0 or 2) long long value; //va_arg 값 int value_len; //va_arg 값의 길이 char base[20]; //진수 int base_len; //진수 int right_margin; //오른쪽 스페이스 개수 } t_box;
C++
복사
앞으로 content 를 다음과같이 정의하겠습니다.
content : [sign][prefix][zero][value] 최종 출력 : [left_margin][content][right_margin]
C++
복사
모든 변수를 0으로 초기화 합니다.
ft_bzero(&content, sizeof(t_content));
먼저 value 에 위에서 va_arg 로 받은값을 대입합니다.
0 출력 로직(content)을 계산하고, 스페이스바 출력 로직(margin)을 계산하겠습니다.
type 에 따른 content 를 완성 시키도록 하겠습니다.
규칙 사전 지식 (참고)

type %, type c

Search
변수
type이 % 일떄 %, 아니면 그대로
flag 가 0 이고, flag 가 - 이 아닐때 zero = width - value_len
COUNT5

type s

type d, type i, type u

Search
이름
type이 d, i 이고, value 가 음수면 1
type이 d, i 이고, value 가 음수면 절대값씌워서 양수로
get_numberlen((unsigned long)value, 10) (음수 고려 안함) precision 이 0, value 가 0 이면 value_len = 0
"0123456789"
precision ≠ -1 이라면 zero = precision - value_len precision 이 -1이고, flag 가 0 이고, flag 가 - 이 아닐때, zero = width - value_len - sign - prefix
COUNT7

type x, type X, type p

Search
이름
태그
type 이 p 이면 2 아니면 0
그대로
get_numberlen((unsigned long)value, 16) precision 이 0, value 가 0 이면 value_len = 0
type이 X 라면, "0123456789ABCDEF" 아니면 "01234566789abcdef"
precision ≠ -1 이라면 zero = precision - value_len precision 이 -1이고, flag 가 0 이고, flag 가 - 이 아닐때, zero = width - value_len - sign - prefix
COUNT7

margin 계산

content 가 완성되었기 때문에 margin 부분만 계산하면 된다.
먼저 content의 전체 길이를 구한다.
content_len = content→sign + ft_max(content→zero, 0) + content→prefix + content→value_len
flag 가 - 인 경우
right_margin = ft_max(flag→width - content_len, 0)
아닌경우
left_margin = ft_max(flag→width - content_len, 0)
최종 출력 길이
ft_max(flag→width - content_len, 0) + content_len

3-4. 출력

이제 이미 다 만들어놓은 content 구조체를 가지고 출력만 잘 하면 된다 !!
[left_margin][sign][zero][prefix][value][right_margin]
C++
복사
위의 순서대로 출력하면된다.
1.
left_margin 만큼 스페이스 출력
2.
sign 이 1 이면 - 출력
3.
prefix 가 2 이면 0x 출력
4.
zero 만큼 0 출력
5.
value 출력
%, c
value 값 출력
s
value 가 있으면 value를 value_len 만큼 출력
없으면 "(null)" 을 value_len 만큼 출력
d i u x X p
value_len > 0 일떄만
base 와 base_len 에 맞춰서 putnbr
6.
right_margin 만큼 스페이스 출력

4. 예외

타입 캐스팅에 유의하세요!!

5. 보너스

하는중....