ft_printf

Minjun_·2021년 6월 18일
0

42Seoul

목록 보기
3/11
post-thumbnail

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 중에서 짧은 것을 사용
nint형 포인터에 출력한 문자 개수를 저장

return

printf의 리턴 값은 출력한 문자열의길이이다.
오류가 난 상황은 -1을 출력한다.

고려한 점


  • 구조체 안에 width 와 precision값을 변수로 만들어 저장하였는데, int형으로 선언하여 저장하였다. 그 경우에서 precision이나 width값에 2147483647이 넘는 경우가 들어올경우 printf에서는 아무 행동도 하지 않고 종료하는 모습을 볼 수 있엇다. 그 경우에는 width와 precision값을 제한해 두어 예외 처리를 해주었다.
  • va_arg(ap, char )함수로 문자열을 받아왔는데, 그 매개변수를 char 형으로 선언한 str에 저장하였다. 매우 긴 문자열이 들어왔을시, 스택 오버플로우가 일어나는 상황이 발생하는 것이 아닌지에 대한 의문이 들었는데, 그 부분에 대해서는 어떻게 처리 해야하는지 생각이 떠오르지 않아 처리를 해주지 않았다. 일단 테스트를 진행해본결과 그런 경우를 찾을 수 없었던 점도 있지만, 어떻게 처리를 해야하는지 감이 안잡혀서 처리를 안해준 것도 있엇다.

후기


  • 기존에 if ~ else를 이용하여 memory할당 없이 구현하려고 했지만, 예외 상황이 한두가지가 아니고 고려야 할점이 너무 많아서, 중간에 구조를 변경하였다.
  • width값에 따라서 출력해야하는 문자열의 길이가 결정되고, 메모리 할당을 해주어 precision을 적용시키는 방향으로 다시 구상했는데, 그 방법으로 조금 더 편리하게 구현한 것 같다. 하지만 메모리 누수가 생기지 않게 해제하는 부분은 까다로웠다.
  • 두명에서 1주일 동안 열심히 달리니 끝낼수 있었다.

참고자료

profile
졸음을 이겨내자..!

0개의 댓글