ft_printf
Contents
프로젝트 주제
printf 함수를 재구현
허용함수 : malloc, free, write, va_start, va_arg, va_copy, va_end
사전 지식
가변인자란?
함수에 전해지는 매개변수의 개수가 바뀌는 것을 가변인자라고 한다.
가변인자 전달 시에는, 고정 매개변수가 하나 있어야 하며, 고정 매개변수 뒤에 ...
을 붙여 선언한다.
int ft_printf(const char *str, ...)
va_list
각 가변인자의 시작 주소를 가리키는 포인터
va_start
void va_start(va_list ap, variable)
va_list로 만들어진 ap라는 포인터에게 첫번째 인수의 시작 주소를 알려주는 함수이다.
va_arg
var_type va_arg(va_list ap, var_type)
va_list로 만들어진 ap 포인터에서 var_type의 인수를 찾아서 리턴하고, ap의 주소를 증가시켜서 다음 인수를 가리키게 만든다.
va_arg(ap, int);
: int형 인자를 추출, 주소값 증가.
va_copy
void va_copy(va_list dest, va_list src)
src에 저장된 가변인자 포인터를 dest로 복사한다. 같은 상태로 만들어준다.
va_end
void va_end(va_list arg_ptr)
모든 인수가 검색된 후, va_list를 null로 초기화 한다.
가변인수 사용이 끝난후 사용된다.
함수 구조
printf 함수 구조
%[flag][width][.precision][length]type
flag
flag의 종류에는 -
+
0
공백
#
5가지가 있다.
- flag
-
-
flag의 경우에는 출력시 왼쪽으로 정렬하여 출력한다.
- flag
0
0 flag의 경우에는 출력시 width값에 따라 빈 공간이 있을시 공백을 0으로 채워서 출력한다.
- flag
+
+ flag의 경우에는 숫자 형태의 type이 들어올때 쓰이는데, 숫자가 양수이면 앞에 + 기호를, 음수이면 앞에 '-'기호를 붙인다.
- flag
공백
' 'flag의 경우도 숫자 형태의 type이 들어올때 쓰이는데, 숫자가 양수이면 앞에 ' '을, 음수이면 앞에 '-'를 붙인다.
- flag
#
# flag의 경우에는 정수를 16진수로 출력해 주는 x, X type일때 숫자 앞에 0x, 0X 를 붙여주는 flag이다.
고려할 점
- '0' flag와 '-' flag가 함께오 면 '0'플래그는 무시된다.
- flag들이 연속으로 들어오는경우, i.e.) "%---s"의 경우도 처리되어야 한다.
- ' ' flag와 '+' flag 에서 음수일때는 원래 - 가 출력이 되니 양수일 경우만 처리해주면 된다.
- ' ' flag와 '+' flag 가 들어왔을때 '0'flag가 붙으면 부호를 맨 앞으로 빼주어야 한다.
- ' ' '+' flag가 함께 들어오면 ' 'flag는 무시된다.
width
width 값은 문자열의 길이와 비교되어 출력되는 문자열의 길이를 결정한다.
width가 없거나 출력할 문자열 보다 길이가 작다면 무시된다.
- width > precision이 적용된 문자열의 길이
width값이 출력할 문자열의 길이를 결정한다.
- else
문자열의 길이가 출력할 문자열의 길이를 결정한다.
precision
precision은 type에 따라서 문자열을 줄이기도 하고, 문자열을 늘리기도 한다.
%s type에서는 precision에 따라서 문자열의 길이가 줄어들고
숫자 type에서는 precision에 따라서 문자열의 길이가 늘어나고 0이 붙어서 나오는 것을 확인할 수 있다.
- precision은 0값이 들어올수도 있다. 따라서 -1로 초기화 해야 한다.
- precision은 가장 먼저 결정되어야 하며, precision을 적용한 문자열을 width와 비교하여 출력할 문자열의 길이를 결정한다.
precision > (num_len)
- num > 0 -> 앞에 0을 붙여서 출력 (01000)
- num <= 0 -> 앞에 0을 붙여서 출력 but 부호 붙임 (-01000)
precision <= (num_len)
length
length는 형변환을 해주는 옵션이다.
type
타입 | 설명 |
---|
c | 문자 |
s | 문자열 |
d, i | 부호있는 10진수 정수 |
u | 부호 없는 10진수 정수 |
o | 부호 없는 8진수 정수 |
x | 부호 없는 16진수 정수 |
X | 부호 없는 16진수 정수(대문자) |
f | 실수를 소수점으로 표기(소문자) |
e | 실수를 지수 표기법으로 표기 |
g | %f 와 %e 중에서 짧은 것을 사용 |
n | int형 포인터에 출력한 문자 개수를 저장 |
return
printf의 리턴 값은 출력한 문자열의길이이다.
오류가 난 상황은 -1을 출력한다.
고려한 점
- 구조체 안에 width 와 precision값을 변수로 만들어 저장하였는데, int형으로 선언하여 저장하였다. 그 경우에서 precision이나 width값에 2147483647이 넘는 경우가 들어올경우 printf에서는 아무 행동도 하지 않고 종료하는 모습을 볼 수 있엇다. 그 경우에는 width와 precision값을 제한해 두어 예외 처리를 해주었다.
- va_arg(ap, char )함수로 문자열을 받아왔는데, 그 매개변수를 char 형으로 선언한 str에 저장하였다. 매우 긴 문자열이 들어왔을시, 스택 오버플로우가 일어나는 상황이 발생하는 것이 아닌지에 대한 의문이 들었는데, 그 부분에 대해서는 어떻게 처리 해야하는지 생각이 떠오르지 않아 처리를 해주지 않았다. 일단 테스트를 진행해본결과 그런 경우를 찾을 수 없었던 점도 있지만, 어떻게 처리를 해야하는지 감이 안잡혀서 처리를 안해준 것도 있엇다.
후기
- 기존에 if ~ else를 이용하여 memory할당 없이 구현하려고 했지만, 예외 상황이 한두가지가 아니고 고려야 할점이 너무 많아서, 중간에 구조를 변경하였다.
- width값에 따라서 출력해야하는 문자열의 길이가 결정되고, 메모리 할당을 해주어 precision을 적용시키는 방향으로 다시 구상했는데, 그 방법으로 조금 더 편리하게 구현한 것 같다. 하지만 메모리 누수가 생기지 않게 해제하는 부분은 까다로웠다.
- 두명에서 1주일 동안 열심히 달리니 끝낼수 있었다.
참고자료