C언어를 공부할 때 가장 처음으로 접하게 되는 표준 출력 함수가 바로 printf 이다. C의 표준 출력 함수는 printf 말고도 fprintf, sprintf, 혹은 purchar, puts 등 여러가지가 있지만 사용자들이 가장 많이 사용하면서 또한 여러가지 기능을 간편하게 사용할 수 있는 함수인 printf를 많이 사용하고 있다.
그런데 C언어를 계속 공부하다 보면 이 printf 라는 함수가 상당히 막강한 기능을 가지고 있는 함수라는 걸 알 수 있다. 실제로 고급 C언어나 Embedded C언어를 공부하다 보면 printf 함수를 직접 구현해보라는 문제에 봉착하게 되는데, 실제로 구현이 좀처럼 쉽지 않은 함수이다. 이번 글에서는 이 막강한(?) printf 에 대한 이야기를 자세히 해 볼까 한다.
우선 printf 이 선언되어 있는 형태를 보면 다음과 같다.
int printf( const char *template, ... );
일단 반환값이 void 가 아니라 int 라는 것부터 신기하다. printf함수에 반환값이 있다니.... 실제로 printf 함수를 사용하면 그 결과값으로 실제로 찍은 문자의 개수가 정수 형태로 반환된다.
또한 입력값을 넣는 부분 또한 특이하다. 맨 앞에 const char * 형이라는 것은 알겠는데 그 다음에 ... 라고 적혀 있는 부분이 신기하다. 실제로 표준 C에서는 가변 입력값을 지원하는데 (관련 헤더 파일 : stdarg.h) 입력값이 몇 개인지 정해지지 않을 경우 쓰는 기법이다. printf의 경우는 입력값으로 맨 앞에 const char * 형태가 들어가야 한다는 필수 요소 이외에 나머지는 상황에 따라 입력값을 달리 넣을 수 있도록 선언된 형태이다.
printf에 대하여 하나 하나 살펴보자.
맨 처음 const char *template 에는 문자열이 들어간다. 이 문자열은 그냥 문자열이 아닌 printf 가 추가로 해석이 가능한 형태의 문자열이 들어간다. 다시 말하면, printf 라는 함수는 template 문자열 내에 % 라는 문자가 들어가면 자체적으로 이를 해석하여 다른 것으로 치환을 하게 된다. 그 치환을 하는 대상은 template 다음에 오는 입력값으로서 첫번째 %에는 template 다음 첫번째 입력값, 두번째 % 에는 두번째 입력값... 이런 식이다.
가장 대표적인 정수 출력을 예로 들어본다. %d 는 정수 출력으로 치환된다.
printf("Value is : %d\n", 100);
출력:
Value is : 100
int age = 30;
int height = 168;
int weight = 70;
printf("age[%d], height[%d], weight[%d]\n", a, h, w);
출력:
age[30], height[168], weight[70]
%d 이외에 다음과 같은 것들이 있다.
-------------------------
[정수형]
%d : 정수형을 10진수로 출력한다. MSB를 체크하여 음수일 경우 음수로 출력한다.
(MSB(Most Significant Bit)는 값을 2진수로 표시했을 때 가장 윗자리수를 뜻한다. 보통 signed 형일 경우 음수이면 1, 양수이면 0이다.)
%o : 정수형을 8진수로 출력한다 MSB를 체크하지 않는다. (무조건 unsigned 형태의 양수로 출력한다.)
%u : 정수형을 10진수로 출력한다. MSB를 체크하지 않는다. (unsigned 형태의 양수로 출력)
%x, %X : 정수형을 16진수로 출력한다. MSB를 체크하지 않는다. (unsigned 형태의 양수로 출력) x 가 소문자이면 알파벳 숫자가 소문자로, 대문자이면 대문자로 출력된다.
[실수형]
%f : 실수형을 고정소수점 형태로 출력한다. ( 예 : 3.500000 )
%e, %E : 실수형을 지수형 형태로 출력한다. ( 예 : 3.500000e+00 ) e가 소문자이면 소문자, 대문자이면 대문자( 3.500000E+00 )로 표시된다.
%g, %G : 가장 간단한 형태를 선택하여 출력한다. (역시 g가 소문자이면 소문자, 대문자이면 대문자로 표시함.)
[문자형]
%c : 문자 하나를 출력한다.
%s : 문자열을 출력한다. 문자열의 첫 문자의 주소값을 함수의 인자로 넣는다.
[기타]
%p : 포인터 값(주소값)을 출력한다.
%% : % 문자 자체를 출력한다. printf에서는 해석할 대상을 %로 구분하므로 % 자체를 출력할 경우를 위해서 이 기능을 지원한다.
예:
printf("%d%%!!!\nOK!!!\n", 100);
출력:
100%
OK
-------------------------
printf 는 포맷을 맞추어 출력한다는 뜻이다. 따라서 위의 정수형, 실수형, 문자형을 사용자가 원하는 형태로 출력할 수 있다. 예를 들어 다음과 같다.
printf("age[%5d]\nheight[%10d]\nweight[%3d]\n", 30, 168, 70);
출력:
age[ 30]
height[ 168]
weight[ 70]
위와 같이 % 와 d 사이에 적혀있는 수에 따라 출력할 수 있는 공간을 확보하여 정할 수 있다.
정수형일 경우 추가 flag의 예를 들으면 다음과 같다.
(1) printf("[%5d]", 20); // [ 20]
(2) printf("[%5.3d]", 20); // [ 020]
(3) printf("[%-5d]", 20); // [20 ]
(4) printf("[%+5d]", 20); // [ +20]
(5) printf("[%+5d]", -20); // [ -20]
(6) printf("[%-+5d]", 20); // [+20 ]
(7) printf("[%5x]", 20); // [ 14]
(8) printf("[%#5x]", 20); // [ 0x14]
(9) printf("[%5.3x]", 20); // [ 014]
(10)printf("[%5o]", 20); // [ 24]
(11)printf("[%-#5o]", 20); // [024 ]
. 뒤의 수는 "반드시 표시해야 할 자릿수"를 뜻한다. (2), (9)
- 는 왼쪽 정렬을 뜻한다. (보통은 오른쪽 정렬이다.) (3), (6), (11)
+ 는 양수일 경우 +를 표시한다. (음수인 경우는 언제든지 -가 표시된다.) (4), (5)
# 는 8진수, 16진수를 표시할 경우에만 적용하며, 8진수는 0이 앞에 붙고, 16진수는 0x (혹은 0X) 가 앞에 붙는다. (8), (11)
실수형일 경우 추가 flag은 다음과 같다.
. 뒤의 수는 "소숫점 이하 몇자리로 표시할지"를 뜻한다.
-, + 는 정수형일때와 같다. (-: 왼쪽 정렬, +: 양수일 경우 + 표시)
# 는 %g, %G 일 경우에만 표시하며 소숫점 이하의 자리수를 반드시 표시할 경우 표시한다.
(1) printf("[%10f]", 2.5); // [ 2.500000]
(2) printf("[%10.3f]", 2.5); // [ 2.500]
(3) printf("[%-10f]", 2.5); // [2.500000 ]
(4) printf("[%+10f]", 2.5); // [+2.500000 ]
(5) printf("[%10e]", 2.5); // [3.500000e+00]
(6) printf("[%10g]", 2.5); // [ 2.5]
(7) printf("[%10.3g]", 2.5); // [ 2.5]
(8) printf("[%#10.3g]", 2.5); // [ 2.500]
문자열형 (%s) 의 경우 추가 flag는 다음과 같다.
. 뒤의 수는 "앞에서 몇자만을 찍을지"를 뜻한다.
- 는 왼쪽 정렬을 뜻한다. (보통은 오른쪽 정렬이다.)
(1) printf("[%s]", "print"); // [print]
(2) printf("[%10s]", "print"); // [ print]
(3) printf("[%10.3s]", "print"); // [ pri]
(4) printf("[%-10.4s]", "print"); // [prin ]
--------------------------
printf 의 경우 % 후에 수 대신 * 가 들어가면 수 또한 인자로 받겠다는 뜻이다.
(1) printf("[%*d]", 5, 100); // [ 100]
(2) printf("[%5.*s]", 3, "print"); // [ pri]
(3) printf("[%*.*s]", 10, 1, "print"); // [ p]
--------------------------
이 이외에도 더 많은 기법이 있다. 자세한 사항은 다음의 사이트를 참고한다.
https://www.gnu.org/software/libc/manual/html_mono/libc.html#Output-Conversion-Syntax
이상 printf 의 사용법에 대한 글을 마친다.
댓글 없음:
댓글 쓰기