42 April - 2.Ft_printf

수현·2023년 4월 2일
0

42Seoul

목록 보기
2/8

📒 Technical consideration

when

  • 23.04.08 토 12:00 ~ 15:30
  • 23.04.16 일 16:00 ~ 22:00
  • 23.04.19 수 16:00 ~ 01:00
  • 23.04.22 토 17:00 ~ 22:30 (평가)
내용
프로그램 이름libftprintf.a
제출 파일.c, .h, Makefile
MakefileNAME, all, clean, fclean, re
외부 함수malloc, free, write, va_start, va_arg, va_copy, va_end
libft사용 가능

📌 주의사항

  • norm error 금지

  • segmetation fault, bus error, double free 금지

  • heap에 동적 할당된 메모리 해제 (메모리 누수 방지)

  • Makefile

    • -Wall -Wextra -Werror 플래그 지정
    • relink 금지 (다시 make했을 때 재실행 금지)
    • $(NAME), all, clean, fclean, re 규칙 포함
  • 전역 변수 사용 금지

    • 본 기능을 구현하기 위한 sub function 정의 시 static으로 정의
      → 정의되지 않은 참조 막기 위해
  • libft 사용

    • 소스와 Makefile을 libft 폴더에 복사해야함
    • 프로젝트 Makefile은 libft의 Makefile을 사용하여 라이브러리를 컴파일한 다음, 프로젝트 컴파일 해야함
  • library를 만들기 위해서 ar command 사용

    • libtool 사용 금지

📒 Mandatory part

📕 printf 정의

1) header file

  • #include <stdio.h>

2) function prototype

  • int ft_printf (const char*, ...)

3) description

  • 서식화된 출력 지원 (c, s, p, d, i, u, x, X, % 구현)
  • 일반 문자열 : 출력 스트림에 그대로 전달되어 출력 (실제 printf처럼 버퍼 관리 수행x)
  • % + 형식 태그 : 추가 인자를 받아 출력 스트림에 어떻게 출력해야 할지 가이드 제공
    • 형식 태그 : %와 서식 지정자로 구성되어 서식 지정자에 따라 다양한 해석 방식 나타냄

4) 서식 지정자 구현

  • %c : single character 출력
    • Character
  • %s : string (문자열) 출력 (str() 내장 함수 사용)
    • String of Charcters
  • %p : void* 형식의 포인터 인자를 16진수로 출력
  • %d : 10진수 정수 출력 (10진수)
    • Signed Decimal Integer
  • %i : 10진수 정수 출력 (8/10/16진수)
    • Signed Decimal Integer
  • %u : 부호 없는 10진수 숫자 출력
    • Unsinged Decimal Integer
  • %x : 소문자를 사용하여 숫자를 16진수로 출력
    • Unsinged Hexadecimal Integer
  • %X : 대문자를 사용하여 숫자를 16진수로 출력
    • Unsinged Hexadecimal Integer
  • %% : 퍼센트 기호 (%) 출력

5) return value

  • 인쇄된 문자 수 반환 (문자열 출력을 종료하는 데 사용되는 null 바이트 제외)
  • 출력하는 항목의 길이 (int형) 반환

📖 참고 📖 conversion specifier(변환 지시어)

  • 개념
    • = 문자열 포맷 문자, 서식 문자
    • %와 conversion 문자(s,d,f) 사이에 전체 자릿수와 소숫점 뒤 자리수 지정 가능
  • 종류
    • %c : repr() 내장 함수 사용
    • %f/F : 부동소수점(floating-point) 실수 출력
    • %o/O : 8진수 출력
    • %e/E : 부동소수점 실수를 지수 형태로 출력
    • %g/G : 부동소수점을 일반 실수 형식이나 지수 형식으로 변환해 출력 (즉, 값에 따라 %e 혹은 %f로 변환)

📕 가변 인자

1) 개념

  • #include <stdarg.h>
  • 정의 : 함수에 인자의 개수가 다르게 할당되어도 처리
  • 특징
    • 최소 1개 이상의 고정 인자 필요 (0개 이상의 가변 인자)
    • 선택 인자를 먼저 받으면 함수의 원형에서 어느 인자를 고정 인자로 받은 지 알 수 없기 때문
    • 가변인자 (...)는 파라미터 순서 상 가장 뒤에 위치

2) 가변 매크로

va_list

  • 정의 : 가변 인자 목록
  • 특징
    • 가변변수를 가리킬 ap 포인터 선언
    • typedef를 통해 내부적으로 char *로 정의

va_start(ap, str)

  • 인자
    • ap : 가변인자(va_list) 변수
    • str : 고정인자
  • 가변 인자를 가져올 수 있도록 가변 인자 시작 주소 참조하는 포인터 설정
  • ap가 str 개수만큼 가리킴
  • 마지막 필수 인자를 매개 변수로 넣기만 하면 자동으로 가변 인자의 시작 주소를 계산하여 ap에 할당해주는 역할
  #define va_start(ap, v) ((ap) = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v))
  - ap : va_list로 만든 포인터 
  - v : 마지막 고정인수 
  - _ADDRESSOF(v) : &(v), 즉 주소로 바꿔주는 매크로
  - _INTSIZEOF(n) : ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)), 마지막 고정인수의 사이즈를 구해서 그 다음 인자의 시작주소 
  (가변인자의 시작주소까지의 메모리상의 거리를 구해주는 매크로)

va_arg(va_list, 자료형)

  • 가변 인자를 참조하는 포인터를 통해 역참조 후, 해당 데이터의 크기만큼 민 후 다음 인자를 참조
  • 사이즈만큼 가변인자를 가져오는 함수
  • va_list가 가리키는 값을 자료형의 크기만큼 리턴 후, 그만큼 뒤로 옮김
  • va_list참조하고 있는 특정 가변 인자역참조하고, va_list내의 다음 가변 인자참조하도록 해주는 매크로
  #define va_arg(ap, t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
  - ap : va_list로 만든 포인터 
  - t : int/long/double 등 타입 이름
  - [참고] char/short의 경우 int로 쓰고 형 변환, float의 경우 double로 쓰고 형 변환 (ex. char ch = (char)va_arg(ap, int);)
  - 가변 인자를 역참조한 후 가변 인자를 가리키도록 만드는 연산 : 대입 연산자를 통해 특정 타입의 크기 만큼 먼저 밀어서 다음 가변 인자를 가리키도록 만들어 놓은 후 다시 그 특정 타입의 크기만큼을 빼서 현재 참조하고 가변 인자를 역참조하도록 만듦
 - _INTSIZEOF(t)만큼 더해놓고 _INTSIZEOF(t)만큼 다시 뺀 값을 t *타입의 포인터 변수를 역참조함

va_end

  • 가변 인자를 모두 처리 후, 가변인자를 가리키는 포인터를 NULL로 초기화
  • 가변 인자 목록을 지칭하는 va_list 타입의 포인터 변수를 NULL을 할당하면서 가변 인자 사용의 끝낼 때 사용
  #define va_end(ap) (ap = (va_list)0)
  - ap : va_list로 만든 포인터 
  
  - 매크로는 실제로 없어도 프로그램에 지장 없음
  - 인텔 계열의 CPU에서는 va_end가 아무 일도 하지 않음
  - 다른 플랫폼과의 호환성에서 중요한 역할을 할 수 있으므로 관례적으로 넣어

va_copy

  • 가변 인자 목록을 복사

📕 바이트 패딩(Byte Padding)

1) 정의

  • 클래스(구조체)에 바이트를 추가해 CPU 접근에 부하를 덜어주는 기법
  • 각자의 타입으로 형 변환 하여 값을 읽어내는 것이다.
    --> char ch = (char)va_arg(ap, int);

2) 특징

  • 메모리에 특정 값이 쓰일 때는 주소 체계 단위로 기록하기 때문
  • 8바이트보다 작은 값을 사용할 때에도 패딩을 추가하여 8바이트 단위로 접근하여 한 번 연산에 하나의 값에 접근가능
  • 이미 사용이 끝난 va_list를 사용하여도 참조하는 것이 없도록 안전성에 기여
  • 장점 : 성능 향상
  • 단점 : 메모리가 낭비

📕 write 함수

1) 개념

  • count 바이트만큼 파일 디스크립터(fd)가 참조하는 파일의 현재 위치에 시작 지점이 buf인 내용을 기록 (fd가 표현하는 객체에 탐색하는 기능이 없으면, 쓰기 작업은 파일의 처음 위치에서 시작)
  • 반환값
    • 성공시 : 쓰기에 성공한 바이트 수 (ssize_t) 반환
    • 에러시 : -1 반환

2) 에러나는 경우

  • O_NONBLOCK : fd가 논블록 모드로 열린 상태에서 쓰기 작업이 블록될 경우
  • EBADF : fd가 유효하지 않거나 쓰기 모드가 아닐 경우
  • EFAULT : buf의 포인터가 호출하는 프로세스 주소 공간 안에 있지 않을 겅우
  • EFBIG : 쓰기 작업이 프로세스 단위로 걸려 있는 최대 파일 제약이나 내부 구현 제약보다 더 큰 파일을 만들 경우
  • EINVAL : fd가 쓰기에 적합하지 않은 객체에 매핑되어 있을 경우
  • EIO : 저수준 입출력 에러
  • ENOSPC : fd가 들어있는 파일시스템에 공간 부족
  • EPIPE : fd가 파이프와 소켓에 연결되어 있지만, 반대쪽 단이 닫힌 경우 (프로세스는 SIGPIPE 시그널을 받음)

📕 구현

1) 구조

(1) Str 인자를 받기 (while문 이용)

  • % 나오기 전까지 문자 출력
  • 출력할 때마다 반환값 길이를 하나씩 증가

(2) %문자 만나는 경우

  • 서식 지정자에 해당하는 문자면 변환 / 아니면 변환 무시

(3) 서식 지정자마다 변환하면서 write 함수로 출력

  • write함수 오류날 경우 에러 처리

(4) 출력한 string 길이만큼 반환 값 구하기

2) 구현할 함수

(1) 인자 받는 함수 --> ft_argument
(2) 서식지정자에 맞게 변환하는 함수 --> ft_printf_cspdiuxX%
(3) 반환값 길이 구하는 함수 --> 구현x

3) 재귀 구현

  • len(출력하는 길이 반환 목적)을 포인터로 사용

📌 ft_printf_c

  • write 오류 처리
  • len +1 증가
int	ft_printf_c(char c, int *len)
{
	if (write(1, &c, 1) == -1)
		return (-1);
	*len += 1;
	return (0);
}

📌 ft_printf_s

  • write 오류 처리
  • len +1 증가
int	ft_printf_s(char *s, int *len)
{
	size_t	i;

	i = 0;
	if (!s)
	{
		if (write(1, "(null)", 6) == -1)
			return (-1);
		*len += 6;
		return (6);
	}
	while (*(s + i))
	{
		if (ft_printf_c(*(s + i), len) == -1)
			return (-1);
		i++;
	}
	return (0);
}

📌 ft_printf_p

  • 주소는 16진수로 표현
  • 16씩 나머지 값을 인덱스로 16진수 변환 후 배열에 저장
  • 저장된 배열을 뒤에서부터 출력 (len +1 증가)
  • write 오류 대비 반환값을 변수에 담아 체크
int	ft_printf_p(unsigned long long p, int *len)
{
	int	temp;

	temp = 0;
	if (p >= 16)
	{
		temp = ft_printf_p(p / 16, len);
		if (temp == -1)
			return (-1);
		ft_printf_p(p % 16, len);
	}
	else
	{
		if (ft_printf_c("0123456789abcdef"[p % 16], len) == -1)
			return (-1);
	}
	return (0);
}

📌 ft_printf_di

  • 음수일 경우 '-' 출력 후 -1 곱함
  • n이 10보다 클 경우 n / 10, n % 10 재귀
  • n이 10보다 작을 경우 문자로 변환 후 출력
  • write 오류 대비 반환값을 변수에 담아 체크
int	ft_printf_di(long long n, int *len)
{
	int	temp;

	temp = 0;
	if (n < 0)
	{
		if (ft_printf_c('-', len) == -1)
			return (-1);
		n *= -1;
	}
	if (n >= 10)
	{
		temp = ft_printf_di(n / 10, len);
		if (temp == -1)
			return (-1);
		ft_printf_di(n % 10, len);
	}
	else
	{
		if (ft_printf_c((n % 10) + 48, len) == -1)
			return (-1);
	}
	return (0);
}

📌 ft_printf_u

  • 양수인 부분 d와 구현 동일
  • write 오류 대비 반환값을 변수에 담아 체크
int	ft_printf_u(unsigned int n, int *len)
{
	int	temp;

	temp = 0;
	if (n >= 10)
	{
		temp = ft_printf_u(n / 10, len);
		if (temp == -1)
			return (-1);
		ft_printf_u(n % 10, len);
	}
	else
	{
		if (ft_printf_c((n % 10) + 48, len) == -1)
			return (-1);
	}
	return (0);
}

📌 ft_printf_xX

  • 16씩 나머지 값을 인덱스로 16진수 변환 후 배열에 저장
  • write 오류 대비 반환값을 변수에 담아 체크
int	ft_printf_x(unsigned int n, const char str, int *len)
{
	int	temp;

	temp = 0;
	if (n >= 16)
	{
		temp = ft_printf_x(n / 16, str, len);
		if (temp == -1)
			return (-1);
		ft_printf_x(n % 16, str, len);
	}
	else
	{
		if (str == 'x')
		{
			if (ft_printf_c("0123456789abcdef"[n % 16], len) == -1)
				return (-1);
		}
		else if (str == 'X')
		{
			if (ft_printf_c("0123456789ABCDEF"[n % 16], len) == -1)
				return (-1);
		}
	}
	return (0);
}

📌 ft_argument

  • 반환값으로 error 유무 판별
  • %p의 경우 재귀 돌리기 위해, 초기 주소 출력값 "0x" 출력
  • len 포인터 이용하여 길이 측정
int	ft_argument(va_list *ap, const char str, int *len)
{
	int	error;

	error = 0;
	if (str == 'c')
		error = ft_printf_c((char)va_arg(*ap, int), len);
	else if (str == 's')
		error = ft_printf_s(va_arg(*ap, char *), len);
	else if (str == 'p')
	{
		if (write(1, "0x", 2) == -1)
			return (-1);
		*len += 2;
		error = ft_printf_p(va_arg(*ap, unsigned long long), len);
	}
	else if (str == 'd' || str == 'i')
		error = ft_printf_di(va_arg(*ap, int), len);
	else if (str == 'u')
		error = ft_printf_u(va_arg(*ap, unsigned int), len);
	else if (str == 'x')
		error = ft_printf_x(va_arg(*ap, unsigned int), len);
	else if (str == 'X')
		error = ft_printf_lx(va_arg(*ap, unsigned int), len);
	else if (str == '%')
		error = ft_printf_c('%', len);
	return (error);
}

📌 ft_printf

  • % 만날 경우 서식지정자에 맞는 함수 호출
  • % 만나지 않을 경우 출력
int	ft_printf(const char *str, ...)
{
	int			len;
	va_list		ap;

	len = 0;
	va_start(ap, str);
	while (*str)
	{
		if (*str == '%')
		{
			str++;
			if (ft_argument(&ap, *str++, &len) == -1)
				return (-1);
		}
		else
		{
			if (ft_printf_c(*str++, &len) == -1)
				return (-1);
		}
	}
	va_end(ap);
	return (len);
}

4) 반복문 구현

  • len(출력하는 길이 반환 목적)을 함수 반환값으로 줌

📌 ft_printf_c

  • write 오류 처리
  • len 반환
int	ft_printf_c(char c)
{
	int	len;

	len = 0;
	if (write(1, &c, 1) == -1)
		return (-1);
	len += 1;
	return (len);
}

📌 ft_printf_s

  • write 오류 처리
  • len 반환
int	ft_printf_s(char *s)
{
	int		i;
	int		len;

	i = 0;
	len = 0;
	if (!s)
	{
		if (write(1, "(null)", 6) == -1)
			return (-1);
		len += 6;
		return (6);
	}
	while (*(s + i))
	{
		if (ft_printf_c(*(s + i)) == -1)
			return (-1);
		i++;
		len++;
	}
	return (len);
}

📌 ft_printf_p

  • 초기 주소 출력값 "0x" 출력
  • 16씩 나머지 값을 인덱스로 16진수 변환 후 배열에 저장
  • 저장된 배열을 뒤에서부터 출력 후 len 반환
int	ft_printf_p(unsigned long long p)
{
	int		i;
	int		len;
	char	c[16];

	i = 0;
	len = 0;
	if (ft_printf_s("0x") == -1)
		return (-1);
	while (p >= 16)
	{
		c[i] = "0123456789abcdef"[p % 16];
		p /= 16;
		i++;
	}
	c[i] = "0123456789abcdef"[p];
	len = ft_printarr(c, i, len);
	if (len == -1)
		return (-1);
	return (len + 2);
}

📌 ft_printf_di

  • 음수일 경우 '-' 출력 후 -1 곱함
  • n이 10보다 클 경우 나머지를 문자로 변환후 배열에 저장
  • 저장된 배열을 뒤에서부터 출력 후 len 반환
int	ft_printf_di(long long n)
{
	int		i;
	int		len;
	char	c[10];

	i = 0;
	len = 0;
	if (n < 0)
	{
		if (ft_printf_c('-') == -1)
			return (-1);
		n *= -1;
		len++;
	}
	while (n >= 10)
	{
		c[i] = (n % 10) + 48;
		n /= 10;
		i++;
	}
	c[i] = (n % 10) + 48;
	len = ft_printarr(c, i, len);
	if (len == -1)
		return (-1);
	return (len);
}

📌 ft_printf_u

  • 양수인 부분 d와 구현 동일
  • n이 10보다 클 경우 나머지를 문자로 변환후 배열에 저장
  • 저장된 배열을 뒤에서부터 출력 후 len 반환
int	ft_printf_u(unsigned int n)
{
	int		i;
	int		len;
	char	c[10];

	i = 0;
	len = 0;
	while (n >= 10)
	{
		c[i] = (n % 10) + 48;
		n /= 10;
		i++;
	}
	c[i] = (n % 10) + 48;
	len = ft_printarr(c, i, len);
	if (len == -1)
		return (-1);
	return (len);
}

📌 ft_printf_xX

  • 16씩 나머지 값을 인덱스로 16진수 변환 후 배열에 저장
  • 저장된 배열을 뒤에서부터 출력 후 len 반환
int	ft_printf_x(unsigned int n)
{
	int		i;
	int		len;
	char	c[10];

	i = 0;
	len = 0;
	while (n >= 16)
	{
		c[i] = "0123456789abcdef"[n % 16];
		n /= 16;
		i++;
	}
	c[i] = "0123456789abcdef"[n];
	len = ft_printarr(c, i, len);
	if (len == -1)
		return (-1);
	return (len);
}

📌 ft_argument

  • 반환값으로 출력하는 길이 반환
int	ft_argument(va_list *ap, const char str)
{
	int	len;

	len = 0;
	if (str == 'c')
		len = ft_printf_c((char)va_arg(*ap, int));
	else if (str == 's')
		len = ft_printf_s(va_arg(*ap, char *));
	else if (str == 'p')
		len = ft_printf_p(va_arg(*ap, unsigned long long));
	else if (str == 'd' || str == 'i')
		len = ft_printf_di(va_arg(*ap, int));
	else if (str == 'u')
		len = ft_printf_u(va_arg(*ap, unsigned int));
	else if (str == 'x')
		len = ft_printf_x(va_arg(*ap, unsigned int));
	else if (str == 'X')
		len = ft_printf_lx(va_arg(*ap, unsigned int));
	else if (str == '%')
		len = ft_printf_c('%');
	return (len);
}

📌 ft_printf

  • % 만날 경우 서식지정자에 맞는 함수 호출
  • % 만나지 않을 경우 출력
int	ft_printf(const char *str, ...)
{
	int			len;
	int			total;
	va_list		ap;

	total = 0;
	va_start(ap, str);
	while (*str)
	{
		len = 1;
		if (*str == '%')
		{
			str++;
			len = ft_argument(&ap, *str++);
			if (len == -1)
				return (-1);
		}
		else
		{
			if (ft_printf_c(*str++) == -1)
				return (-1);
		}
		total += len;
	}
	va_end(ap);
	return (total);
}

📌 ft_printarr

int	ft_printarr(char *c, int i, int len)
{
	while (i >= 0)
	{
		if (ft_printf_c(c[i]) == -1)
			return (-1);
		i--;
		len++;
	}
	return (len);
}

📌 ft_printf.h

#ifndef FT_PRINTF_H
# define FT_PRINTF_H

# include <unistd.h>
# include <stdlib.h>
# include <stdarg.h>

int		ft_argument(va_list *ap, const char str);
int		ft_printf(const char *str, ...);
int		ft_printf_c(char c);
int		ft_printf_s(char *s);
int		ft_printf_p(unsigned long long p);
int		ft_printf_di(long long n);
int		ft_printf_u(unsigned int n);
int		ft_printf_x(unsigned int n);
int		ft_printf_lx(unsigned int n);
int		ft_printarr(char *c, int i, int len);

#endif

📌 MAkefile

NAME = libftprintf.a

SRCS = ft_printf.c ft_printf2.c

OBJS = $(SRCS:.c=.o)

all : $(NAME)

$(NAME) : $(OBJS)

%.o:%.c ft_printf.h
	cc -Wall -Wextra -Werror -c $< -o $@
	ar rc $(NAME) $@

clean :
	rm -rf $(OBJS)

fclean : clean
	rm -rf $(NAME)

re :
	make fclean
	make all

.PHONY : all clean fclean re

5) main 함수

#include <stdio.h>

int main(void)
{
	char			c;
	char			*s;
	char			*ns;
	void			*p;
	void			*np;
	int				d;
	int				i;
	unsigned int	u;
	int				x;

	c = 'a';
	s = "abcde";
	ns = 0;
	p = &d;
	np = 0;
	d = -2147483648;
	i = 2147483647;
	u = 4294967295;
	x = 10;
	ft_printf("<ft_printf>\nc = %c\ns = %s\n s가 NULL일 경우 = %s\n", c, s, ns);
	ft_printf("p = %p\n p가 NULL일 경우 = %p\n", p, np);
	ft_printf("d = %d\n i = %i\n u = %u\n", d, i, u);
	ft_printf("x = %x\n X = %X\n %% = %%\n", x, x);
	printf("----------------------------------------------------------------\n");
	printf("<printf>\nc = %c\ns = %s\n s가 NULL일 경우 = %s\n", c, s, ns);
	printf("p = %p\n p가 NULL일 경우 = %p \n", p, np);
	printf("d = %d\n i = %i\n u = %u\n", d, i, u);
	printf("x = %x\n X = %X\n %% = %%\n", x, x);
}
  • 실행 결과

📌 d옵션과 i옵션 차이

  • 공통점 : Singed Decimal Integer (부호 있는 10진 정수)
  • 차이점 : 출력은 동일하지만 입력에 차이가 있음
    • d옵션 : 부호 있는 10진 정수로 입력 받음
    • i옵션 : 부호 있는 8진/10진/16진 정수로 입력 받음

📌 %기호 출력

형식 문자열을 이용할 때는 % 기호를 이용하여 형식 태그의 시작임을 알리게 된다. % 기호를 출력하기 위해선 \로는 출력할 수 없고 오로지 % 기호를 이용해서만 출력할수 있다.

단순히 printf를 이용하여 기호를 출력하려 하면 아래와 같이 경고 문구를 띄우면서 컴파일이 되지 않는 것을 볼 수 있다.

특수 문자들을 출력하는 Escape Sequence()를 이용하여 출력하려 해도, 경고 문구를 띄우면서 컴파일을 막는 모습을 확인할 수 있다.

📒 Bonus part

📕 플래그

문자 % 다음 0개 이상의 플래그가 올 수 있음

1) #

  • 값은 "대체 형식"으로 변환
  • o 변환 의 경우 : 출력 문자열의 첫 번째 문자는 0이 됩니다(아직 0이 아닌 경우 0을 접두사로 사용)
  • x 및 X 변환 의 경우 : 0이 아닌 결과 앞에 문자열 "0x"(또는 X 변환의 경우 "0X")가 추가
  • a , A , e , E , f , F , g 및 G 변환 의 경우 : 결과 뒤에 숫자가 없더라도 결과에 항상 소수점이 포함

2) 0

  • 값을 0으로 채우기
  • d , i , o , u , x , X , a , A , e , E , f , F , g 및 G 변환의 경우 변환된 값의 왼쪽은 공백이 아닌 0으로 채워짐
  • 0 및 - 플래그가 모두 나타나면 0 플래그 는 무시
  • 숫자 변환( d , i , o , u ,x , X ), 0 플래그는 무시

3) -

  • 변환된 값은 필드 경계에서 왼쪽으로 조정됨 (기본값은 오른쪽 맞춤)
  • n번의 변환을 제외하고 변환된 값은 왼쪽이 공백/0이 아니라 오른쪽이 공백으로 채워짐
  • A - 둘 다 주어지면 0 을 무시합니다 .

4) ' '

  • (공백) 부호 있는 변환으로 생성된 양수(또는 빈 문자열) 앞에는 공백이 있음

5) +

  • 부호(+ 또는 -)는 항상 부호 있는 변환에 의해 생성된 숫자 앞에 위치
  • A +는 둘 다 사용되는 경우 공백을 재정의

📕 필드 폭

  • 최소 필드 너비를 지정하는 선택적 10진수 문자열(0이 아닌 첫 번째 숫자 포함)
  • 변환된 값의 문자 수가 필드 너비보다 적으면 왼쪽(또는 왼쪽 조정 플래그가 지정된 경우 오른쪽)에 공백이 채워짐
  • 10진수 문자열 대신 "" 또는 "m$"(일부 10진수 m 의 경우)를 작성하여 필드 너비가 각각 다음 인수 또는 m 번째 인수에 지정함
    • int 유형
  • 음수 필드 너비는 양수 필드 너비가 뒤따르는 '-' 플래그로 간주함
  • 어떤 경우에도 존재하지 않거나 작은 필드 너비로 인해 필드가 잘리지 않음
    • 변환 결과가 필드 너비보다 넓으면 변환 결과를 포함하도록 필드가 확장됨

📕 정밀도

  • 마침표('.') 뒤에 선택적 10진수 문자열이 오는 형식의 선택적 정밀도
  • 10진수 문자열 대신 "" 또는 "m$"(일부 10진 정수 m의 경우)를 작성하여 정밀도가 각각 다음 인수 또는 m번째 인수에 제공되도록 지정
  • int 유형의 . 정밀도가 '.'로만 제공되거나 정밀도가 음수이면 정밀도는 0으로 간주
  • d, i, o, u, x 및 X 변환에 대해 표시되는 최소 자릿수 a, A, e에 대한 기수 문자 뒤에 표시되는 자릿수를 제공
  • E, f 및 F, g, G 변환의 최대 유효 자릿수 또는 s, S 변환 의 경우 문자열에서 인쇄할 최대 문자 수

profile
Notion으로 이동 (https://24tngus.notion.site/3a6883f0f47041fe8045ef330a147da3?v=973a0b5ec78a4462bac8010e3b4cd5c0&pvs=4)

0개의 댓글