[ft_printf](8)서식지정자(x, X, o, p)출력, 코드 중간 정리


이번 포스트는 (ft_print)서식지정자(x, X, o)출력, 코드 중간 정리에 관한 내용입니다.




1️⃣ 부호없는 16진수, 8진수 서식지정자(x, X, o) 규칙

(1) %x 규칙[%x, %X, %o은 규칙이 비슷]

printf("% 7.6x끝\n", 1234);  //컴파일 오류
printf("%+8.6x끝\n", 1234);  //컴파일 오류
printf("%8.6x끝\n", 1234);
printf("%#*.*x끝\n", 8, 3, 12324);
printf("%-8.6x끝\n", 1234);
printf("%8.3x끝\n", 1234);
printf("%3.3x끝\n", 1234);
printf("%x끝\n", 1234);
printf("------------------\n");
printf("%8x끝\n", 1234);
printf("%-8x끝\n", 1234);
printf("%08x끝\n", 1234);
printf("%#x끝\n", 1234);
printf("%-#8x끝\n", 1234);
printf("%#8x끝\n", 1234);
printf("------------------\n");
printf("%#08x끝\n", 1234);
printf("%lx끝\n", (unsigned long int)123456);
printf("%llx끝\n", (unsigned long long int)123456);
printf("%hhx끝\n", 123456);
printf("%hx끝\n", 123456);
/*-------출력-------*/
  0004d2끝
  0x3024끝
0004d2  끝
     4d2끝
4d2끝
4d2끝
------------------
     4d2끝
4d2     끝
000004d2끝
0x4d2끝
0x4d2   끝
   0x4d2끝
------------------
0x0004d2끝
1e240끝
1e240끝
40끝
e240끝
  • '  '플래그(공백)와 ' + '플래그 사용이 불가능 합니다.
  • '* . *'옵션 사용이 가능합니다.
  • ' # '플래그사용시 %x, %는 각각 0x, 0X%o0을 붙여서 출력해주었습니다. 단, ' 0 '플래그에 의해 0으로 공백이 채워진경우 0보다 우선순위로 출력됩니다.
  • 나머지 규칙은 %d, %i, %u 때와 동일 합니다.

(2) fg->hash요소 삭제

  • 위에서 규칙을 알아본 결과 서식지정자(%x, %X, %o)' + '플래그, '  '플래그를 사용하지 못하며 부호없는 정수이기에 ' - '(음수)기호가 올 일이 없습니다. 또한 ' # '플래그, '  '플래그, ' + '플래그의 출력 규칙은 같습니다.
  • 그렇기 때문에 ' # '플래그fg->plus를 사용해도 중복없이 사용이 가능합니다.
  • 또한 코드도 깔끔해 질 것입니다.
/*-----변경 전-----*/
if (**format == '#')
    fg->hash = 1;
/*-----변경 후-----*/
if (**format == '#')
    fg->plus = '0';
  • ' # '플래그일 때 fg->plus값에 '0'을 지정하여 %o(8진수)일 때 0을 붙여주도록 했습니다.
  • %x, %X(16진수)일 때는 fg->plus == '0'의 조건문을 이용하여 0x혹은 0X를 붙여줄지의 여부를 판단하도록 했습니다.




2️⃣ malloc(동적메모리할당)함수 없애기

참고포스트 > [C]동적메모리 할당 함수

  • 기존의 ft_ullitoa_malloc함수에서 malloc함수를 이용하여 동적할당을 해주었습니다.
  • 하지만 malloc(동적할당)을 사용하는데 단점이 많으며 그만큼 위험하기도 합니다.
  • 실제로 ft_ullitoa_malloc함수는 정수를 단순히 문자로 만들어 메모리에 저장하는 함수로 많은 메모리를 차지하지 않습니다. 그렇기 때문에 미리 배열을 선언해서 복사하는 것이 더 나을 것 같습니다.
  • 하지만 ft_ullitoa_malloc함수 내부에서 배열을 선언하여 반환해주면 댕글링 포인터(danglinh porinter)가 일어날 수 있기 때문에 매개변수로 배열을 받아 복사하는식으로 다시 구현할 예정입니다.

(1) 최대 배열의 크기 구하기

  • ft_print에 사용될 최대 크기의 자료형은 unsigned long long int 일 것이며 8진수(%llo)형태로 출력했을 때 가장 길게 출력될 것입니다.

< 가장 긴 길이의 정수 출력 >

#include <stdio.h>
#include <limits.h>

int main(void)
{
    unsigned long long int num;
    int cnt;

    num = ULLONG_MAX;
    printf("%llo%n\n", num, &cnt);
    printf("길이: %d\n", cnt);    
}
/*-------출력-------*/
1777777777777777777777
길이: 22
  • 배열의 크기'\0'의 자리까지 고려해서 23으로 잡으면 충분할 것 같습니다.

(2) ft_ullitoa_malloc함수 수정하기

< 수정 전 ft_ullitoa_malloc함수 >

char *ft_ullitoa_malloc(unsigned long long int num, char *num_version)
{
	size_t num_len;
	int cnt;
	unsigned long long int nb;
	char *result;

	num_len = ft_strlen(num_version);
	nb = num;
	cnt = 1;
	while ((nb /= num_len) > 0)
		cnt++;
	if(!(result = (char*)malloc(sizeof(char) * (cnt + 1))))
		return (NULL);

	result [cnt] = '\0';
	while (cnt > 0)
	{
		result[cnt - 1] = num_version[num % num_len];
		cnt--;
		num /= num_len;
	}
	return (result);
}

< 수정 후 ft_ullitoa_cp함수 >

void *ft_ullitoa_cpy(unsigned long long int num, char *num_version, char* str)
{
	size_t num_len;
	int cnt;
	unsigned long long int nb;

	num_len = ft_strlen(num_version);
	nb = num;
	cnt = 1;
	while ((nb /= num_len) > 0)
		cnt++;
	str[cnt] = '\0';
	while (cnt > 0)
	{
		str[cnt - 1] = num_version[num % num_len];
		cnt--;
		num /= num_len;
	}
}




3️⃣ char형, short형 받는 방식 변경

  • 기존에 t_flag구조체에 char, short, unsigned char, unsigned short형의 요소를 추가하여 va_arg(ap, int)를 강제로 받아오고자 했습니다.
  • 하지만 이런식으로 작은 데이터 자료형큰 데이터 자료형의 값을 복사하려고 하다보니 예기치 못한 결과값이 출력되었습니다.
  • 반대로 큰 데이터 자료형작은 데이터 자료형의 값을 복사하는 것은 정상적으로 출력 되었습니다.
  • 그래서 va_arg(ap, int)강제 형변환을 하여 fg->lli에 직접 넣어주는 식으로 바꾸어 주었습니다.
  • 기존의 t_flag구조체의 short, unsigned char, unsigned short형 요소들은 없앴습니다. (char형은 문자(c)출력을 위해 필요)

< 수정 전 길이 조정 조건문 >

if (fg->data_length == -2)
{       
    fg->c = va_arg(ap, int);
    fg->lli = fg->c;
}
else if (fg->data_length == -1)
{
    fg->s = va_arg(ap, int);
    fg->lli = fg->s;
}
else if (fg->data_length == 0)
    fg->lli = va_arg(ap, int);
else if (fg->data_length == 1)
    fg->lli = va_arg(ap, long int);
else if (fg->data_length == 2)
    fg->lli = va_arg(ap, long long int);    
if (fg->lli < 0)
{
    fg->ulli = fg->lli * (-1);
    fg->plus = '-';
}
else
    fg->ulli = fg->lli;

< 수정 후 길이 조정 조건문 >

if (fg->data_length == -2)
    fg->lli = (char)va_arg(ap, int);
else if (fg->data_length == -1)
    fg->lli = (short)va_arg(ap, int);
else if (fg->data_length == 0)
    fg->lli = va_arg(ap, int);
else if (fg->data_length == 1)
    fg->lli = va_arg(ap, long int);
else if (fg->data_length == 2)
    fg->lli = va_arg(ap, long long int);    
if (fg->lli < 0)
{
    fg->ulli = fg->lli * (-1);
    fg->plus = '-';
}
else
    fg->ulli = fg->lli;




4️⃣ 16진수 서식지정자(x, X)출력함수 구현

< print_xX함수 >

void print_xX(va_list ap, t_flag *fg, const char c, int *len)
{
    set_print_cpy(ap, fg, c);
    if (fg->plus != 0)
        fg->padding_front -= 2; 
    if (fg->left != 1) // '-'왼쪽정렬이 아닐때
    {
        if (fg->zero == 1)
        {
            if (fg->plus != 0) 
                (c == 'x') ? ft_print_str("0x", len) : ft_print_str("0X", len);
            fg->padding_front -= ft_print_word('0', fg->padding_front, len);
        }
        else if (fg->zero == 0)
        {
            fg->padding_front -= ft_print_word(' ', fg->padding_front - fg->padding_back, len);
            if (fg->plus != 0) 
                (c == 'x') ? ft_print_str("0x", len) : ft_print_str("0X", len);
            fg->padding_back -= ft_print_word('0', fg->padding_back, len);
        }
    }
    else // '-'왼쪽정렬일 때
    {
        if (fg->plus != 0)
            (c == 'x') ? ft_print_str("0x", len) : ft_print_str("0X", len);
        fg->padding_front -= fg->padding_back;
        fg->padding_back -= ft_print_word('0', fg->padding_back, len);
    }
    ft_print_str(fg->result, len); // 서식지정자 출력 (메인 출력)
    if(fg->left == 1) // 뒤쪽 공백출력 판단
        ft_print_word(' ', fg->padding_front, len);
}




5️⃣️ 포인터 주소 서식지정자(p) 규칙

< %p(포인터 주소) >

printf("%p끝\n", temp);
printf("%17p끝\n", temp);
printf("%-17p끝\n", temp);
printf("%+p끝\n", temp);          // 컴파일 오류
printf("%8.6p끝\n", temp);        // 컴파일 오류
printf("%#*.*p끝\n", 8, 3, temp); // 컴파일 오류
printf("%-8.6p끝\n", temp);      // 컴파일 오류
printf("%8.3p끝\n", temp);       // 컴파일 오류
printf("%3.3p끝\n", temp);      // 컴파일 오류
printf("%08p끝\n", temp);      // 컴파일 오류
printf("%#p끝\n", temp);       // 컴파일 오류
printf("%-#8p끝\n", temp);     // 컴파일 오류
printf("%#8p끝\n", temp);      // 컴파일 오류
printf("%#08p끝\n", temp);     // 컴파일 오류
/*-------출력-------*/
0x7fffd986e715끝
   0x7fffd986e715끝
0x7fffd986e715   끝
  • 주소포인터 서식지정자(%p)' - '플래그(왼쪽여백)너비옵션만 사용이 가능합니다.
  • 그리고 출력값16진수로 출력되며 출력값 앞에 0x를 붙여주는 것만 고려해주면 됩니다.




6️⃣ 포인터주소 서식지정자(p)출력함수 구현

< print_p함수 >

void print_p(va_list ap, t_flag *fg, const char c, int *len)
{
    set_print_cpy(ap, fg, c);
    fg->padding_front -= 2;
    if (fg->left != 1) // '-'왼쪽정렬이 아닐때
        ft_print_word(' ', fg->padding_front, len);
    ft_print_str("0x", len);
    ft_print_str(fg->result, len); // 서식지정자 출력 (메인 출력)
    if(fg->left == 1) // 뒤쪽 공백출력 판단
        ft_print_word(' ', fg->padding_front, len);
}




© 2021.02. by kirim

Powered by kkrim