#include <stdio.h>
int printf(const char *format, ...);
printf
함수는 C에서의 표준 출력 함수로, 표준 출력 스트림인 stdout(standard output)에 정해진 format
형식의 문자열을 출력한다. format
은 const char*
타입으로, printf
사용 시 출력하고자 하는 형태의 문자열을 format specifier 및 escape sequence와 함께 구성하여 "...."
형태로 전달한다.
Escape Sequence
출력하고자 하는 문자열은 "
큰 따옴표로 둘러싸인 문자열로 전달된다. 만약 "Hello World"
라는 문자열을 (큰 따옴표까지 포함하여) 출력하려면 다음과 같이 하면 될까?
printf(""Hello World"");
답은, '아니다'이다. 위와 같이 문자열을 전달하게 되면 처음 등장하는 ""
가 format
으로 컴파일러에 의해 인식되기 때문이다. 마찬가지로 문자열 안 개행(enter)이 출력되도록 하고 싶을 때, 문자열 안에서 단순히 'enter'를 입력해두는 것으로는 개행이 printf
에 의해 출력되지 않는다. (오히려 'enter'로 인해 분리된 코드로 인식되어 에러가 발생한다)
따라서 escape sequence를 사용하여 문자열 안 직접 표기해두기 어려운 character가 출력되도록 printf
함수에 전달해야 한다.
escape sequence | printed output | escape sequence | printed output |
\n |
개행 (newline) | \t |
수평 탭 (tab) |
\' |
' |
\" |
" |
\\ |
\ |
\0 |
null character |
이젠 escape sequence를 사용하여 "Hello World"
를 출력할 수 있게 되었다.
printf("\"Hello World\"\n");
그렇다면 고정된 문자열이 아닌, 특정 int
변수 x
의 값을 출력하고 싶다면 어떻게 해야 할까? 이때 사용되는 것이 바로 format specifier이다.
Format Specifier
Format specifier는 추후 format
의 출력 시 1) 사용자가 정의한 format으로, printf 함수에 전달된 2) argument의 값으로 대체되어 출력된다. format
안에서 %
로 시작되는 요소들이 바로 format specifier이고, 다음과 같은 형태로 구성된다.
%[flags][width][.precision][length]specifier
specifier
specifier |
output | example |
d or i |
signed decimal integer | 392, -491 |
u |
unsigned decimal integer | 4294967295 |
o |
unsigned octal | 610 |
x / X |
unsigned hexadecimal integer | 7fa / 7FA |
f / F |
decimal floating point | 392.65 / 392.65 |
e / E |
scientific notation (mantissa/exponent) | 3.9265e+2 / 3.9265E+2 |
g / G |
use the shortest representation → %e or %f / %E or %F |
392.65 / 392.65 |
a / A |
hexadecimal floating point | 0xc.90fep-2 / 0xC.90FEP-2 |
c |
character | a, b, c |
s |
string of characters | hello |
p |
pointer address | b2400000 |
n |
여기에 해당 되는 argument는 signed int* 타입이어야 한다.argument가 가리키는 주소에 지금까지 출력된 character 수가 저장된다. |
nothing printed |
% |
%% 가 연달아 표기되면 "%"가 출력된다. |
% |
※ specifier
의 대소문자에 따라 lowercase 또는 uppercase로 출력된다.
앞서 언급했던 int x
의 값을 출력하려면 다음과 같이 할 수 있다.
int main(void)
{
int x = 10;
printf("value of x is : %d\n", x);
}
[ output ]
value of x is : 10
Format specifier를 사용할 때 specifier
는 반드시 명시되어야 한다. 하지만 specifier
외에도 필요에 따라 flags
, width
, .precision
, length
의 sub-specifier도 추가할 수 있다.
width
width |
description |
number |
출력될 최소 너비(width)를 지정한다. 명시된 number 보다 출력될 값의 length가 더 짧으면 그 나머지가 공백으로 채워진채로 출력된다. 만약 출력될 값의 length가 더 길다면 그대로 출력된다. |
* |
출력될 최소 너비(width)가 출력될 값 직전의 argument로 전달된다는 의미이다. |
만약 width
설정이 출력될 값의 length보다 길 경우에는 오른쪽 정렬(right justification)된 채로 출력되는 것이 default 설정이다.
int main(void)
{
printf("%9d\n", 1234567890);
printf("%9d\n", 789);
}
[ output ]
1234567890
789
flags
flags |
description |
|
- |
주어진 width 안에서 왼쪽 정렬(left-justify)하여 출력한다. | |
+ |
출력되는 값에 따라 + 또는 - 사인을 강제로 출력한다. (default로는 음수의 값만 - 사인과 함께 출력된다) |
|
(space) |
출력될 사인이 없다면 출력되는 값 앞에 공백이 삽입된다. | |
# |
used with o , x , X |
0이 아닌 값은 0x 또는 0X prefix와 함께 출력된다. |
used with a , A , e , E , f , F , g , G |
출력되는 값에 소수부의 숫자가 없어도 소수점이 출력된다. (default로는 소수부의 숫자가 없다면 소수점이 출력되지 않는다) |
|
0 |
width 가 선언되었을 때 공백 대신 0 으로 남는 공간을 채운다. |
int main(void)
{
char s1[] = "My Age";
char s2[] = "Birthday";
printf("%-12s : %+10d\n", s1, 23);
printf("%-12s : %10d\n", s2, 19900101);
}
[ output ]
My Age : +23
Birthday : 19900101
.precision
.precision |
description |
|
.number |
used with integer specifiers ( d , i , o , u , x , X ) |
출력될 최소한의 숫자 개수, 즉 width 와 유사하게 동작한다. 단, width 와 달리 나머지 공간을 공백이 아닌 0 으로(leading zeros) 채운다는 것이 다르다.또, 만약 precision 이 0으로 설정되면 0의 값은 출력되지 않는다. |
used witha , A , e , E , f , F |
소수점 뒤 출력될 숫자의 개수를 지정한다. (default로는 6이다) | |
used with g , G |
출력될 최대의 significant 수의 개수를 지정한다. | |
used with s |
출력될 character의 최대 개수를 지정한다. | |
precision 값이 명시되지 않은채 . 만 사용되면 precision 값은 0으로 가정된다. |
||
.* |
precision이 출력될 값 직전의 argument로 전달된다는 의미이다. |
int main(void)
{
unsigned int x = 0x1B2300;
printf("%x\n", x);
printf("%#.8X\n", x);
}
[ output ]
1b2300
0X001B2300
%n와 *
%n와 *은 출력물 formatting에 매우 유용하게 쓰일 수 있다. 예를 들어, 다음과 같은 출력 결과물을 보고 싶다고 해보자.
Name: Gildong,
Hong
단순하게는 Name:
문자열의 길이를 알아내어 다음과 같이 할 수 있다.
int main(void)
{
char question[] = "Name";
char answer[2][10];
scanf("%s %s", answer[0], answer[1]);
printf("%s: %s,\n", question, answer[0]);
printf("%5s %s\n", "", answer[1]);
}
하지만, 만약 question
문자열을 "My Name"으로 바꾸면서 형식을 유지하려면 printf
의 %5s
도 바뀐 문자열의 길이에 맞추어 변경해주어야 한다. 이때 %n
와 *
을 활용하면 question
문자열을 어떻게 바꾸던 printf
의 format
을 변경하지 않고도 형식을 그대로 유지할 수 있다.
int main(void)
{
char question[] = "My Name";
char answer[2][10];
int len;
scanf("%s %s", answer[0], answer[1]);
printf("%s: %n%s,\n", question, &len, answer[0]);
printf("%*s%s\n", len, "", answer[1]);
}
위의 예시를 보면 첫 번째 printf
의 format
에서 %n
전까지 출력된 character 수가 len
에 저장되고, 그 길이만큼을 두 번째 출력 시 빈 문자열로 채우게 되므로 원하는 형식이 유지된다.
Reference
・ https://web.mit.edu/10.001/Web/Course_Notes/c_Notes/tips_printf.html
・ https://www.cplusplus.com/reference/cstdio/printf/
・ https://www.geeksforgeeks.org/g-fact-31/
'C · C++' 카테고리의 다른 글
[C++] 구조체(struct)와 클래스(class) (2) | 2022.05.15 |
---|---|
[C(++)] Header Guard (헤더 가드) Coding (0) | 2022.02.05 |
[C] const type qualifier (0) | 2021.12.21 |
[C++] namespace 사용 및 특징 (0) | 2021.12.18 |
[C++] 범위 확인 연산자 (::) (0) | 2021.12.15 |