printf의 print 기능을 write 함수로 구현해보자!

리로린·2024년 8월 12일
0

c 언어

목록 보기
2/10
#include <unistd.h>
#include <limits.h>  // For INT_MIN

// 숫자를 문자열로 변환하고 출력하는 보조 함수
void func(char *buf, int *i, int *negative, int nb)
{
    // 음수 처리
    if (nb < 0) {
        *negative = 1;         // 음수 플래그를 1로 설정
        nb = -nb;              // 숫자를 양수로 변환 (음수 부호를 없앰)
    }

    // 숫자가 0인 경우
    if (nb == 0) {
        buf[(*i)++] = '0';    // 0을 문자열로 변환하여 버퍼에 저장
    } else {
        // 숫자를 문자열로 변환 (역순으로 저장)
        while (nb > 0) {
            buf[(*i)++] = (nb % 10) + '0'; // 숫자의 마지막 자릿수를 문자로 변환
            nb /= 10;                    // 숫자의 마지막 자릿수를 제거
        }
    }

    // 음수 기호 추가
    if (*negative) {
        buf[(*i)++] = '-';  // 음수였던 경우 '-' 기호를 추가
    }

    // 문자열을 역순으로 출력
    while (*i > 0) {
        write(1, &buf[--(*i)], 1); // 버퍼에서 문자 하나를 역순으로 출력
    }
}

// 정수를 출력하는 함수
void ft_putnbr(int nb)
{
    char buf[12];  // 최대 11자리 숫자 + '-' 기호 + null terminator
    int i = 0;     // 버퍼의 현재 인덱스
    int negative = 0; // 음수 여부를 표시하는 플래그

    // INT_MIN을 특별히 처리
    if (nb == INT_MIN) {
        write(1, "-2147483648", 11);  // INT_MIN의 경우 고정 문자열을 직접 출력
        return;  // 함수 종료
    }

    // 숫자를 문자열로 변환하고 출력
    func(buf, &i, &negative, nb);
    // 왜 주소를 사용하는가 ?
    // 주소를 사용하면 함수 호출 시 변수의 주소를 넘겨 메모리 복사 비용을 줄이는 것에 유용해 값의 전달이 가능하다.  
}

// 테스트 함수
int main()
{
    ft_putnbr(12345);    // 출력: 12345
    write(1, "\n", 1);
    ft_putnbr(-6789);    // 출력: -6789
    write(1, "\n", 1);
    ft_putnbr(0);        // 출력: 0
    write(1, "\n", 1);
    ft_putnbr(2147483647); // 출력: 2147483647
    write(1, "\n", 1);
    ft_putnbr(INT_MIN);  // 출력: -2147483648
    write(1, "\n", 1);

    return 0;
}

먼저 write 함수를 사용하는 이유에 대해서 알아보자. write는 시스템 콜로, 버퍼를 사용하지 않고 직접 파일 디스크립터에 데이터를 출력한다. printf와 같은 표준 라이브러리 함수는 내부적으로 버퍼링을 하며, 이 과정에서 메모리 할당과 처리가 복잡해질 수 있다. write를 사용하면 이러한 복잡성을 피할 수 있다. 이와 같은 write로 printf를 구현 해 보자.


ft_putnbr 구현 설명

  • 기본 기능:
    ft_putnbr 함수는 정수를 문자열로 변환하고, write 시스템 콜을 사용하여 이를 출력한다. 이 함수는 음수, 양수, 제로, 그리고 INT_MIN과 같은 특별한 경우를 처리할 수 있다.

  • 함수 동작 과정:

    • 음수 처리:
      입력 값이 음수일 경우, 이를 양수로 변환하고 음수 플래그를 설정하여 나중에 문자열 앞에 '-' 기호를 추가한다.
    • 숫자 문자열 변환:
      숫자를 문자열로 변환할 때, 각 자릿수를 역순으로 버퍼에 저장한다. 이는 나중에 역순으로 출력하여 올바른 숫자 순서를 유지하기 위함이다.
    • 특별한 경우 처리:
      INT_MIN의 경우, 직접 문자열을 출력한다. 이는 INT_MIN의 절대값이 int 범위를 초과하기 때문에 특별히 처리해야 하는 경우입이다.
    • 출력:
      버퍼에 저장된 문자열을 역순으로 출력한다. 이 과정에서 write 시스템 콜을 사용하여 직접 데이터를 출력한다.
  • printfprint 구현 비교:

    • printf:
      다양한 포맷을 지원하며, 정수, 실수, 문자열 등 다양한 데이터 타입을 출력할 수 있다. 내부적으로는 변수를 포맷팅하여 문자열로 변환한다.
    • ft_putnbr:
      정수만 처리하며, 포맷팅 기능이 없다. 단순히 정수를 문자열로 변환한다.

주의 해야 할 점

  • INT_MIN의 범위 문제:
    INT_MIN(limit.h 뿐만아니라 int의 최소값)은 -2147483648으로, 음수의 절대값이 INT_MAX보다 1만큼 크다. 예를 들어, INT_MAX는 2147483647다.
    음수로 변환 후 절대값으로 처리할 때, INT_MIN은 변환된 값이 INT_MAX와는 다르게 되어, 일반적인 절대값 처리 방법으로는 올바르게 변환할 수 없다.
  • 부호 반전의 한계:
    일반적으로 부호 반전을 하는 경우 단순하게 음수 기호를 추가하면 되지만 INT_MIN 같은 경우 위와 같은 범위의 문제가 발생한다. (-2147483648)을 계산하면, 값이 2147483648이 되는데, 이는 int 타입의 범위를 초과한다. 따라서 단순히 부호를 반전시키는 방법은 INT_MIN을 처리할 수 없다.
profile
리로린의 블로그

0개의 댓글