본문 바로가기
📚 STUDY/🔥 C

[3-1] printf 함수

by 엄지잉 2024. 9. 2.

🌟 글 많음 주의........ 미리 경고합니닷 🌟

 

- printf 함수는 서식 문자열이라고 불리는 문자열을 문자열의 특정 부분에 추가된 값과 함께 출력하는 기능을 수행하도록 만들어짐.

- printf가 호출되려면, 서식 문자열과 출력을 할 때 삽입되어야하는 값을 공급해줘야 함.

printf(string, expr1, expr2, …);
  • 출력되는 값들은 상수, 변수 혹은 매우 복잡한 표현식일 수도 있음. printf 함수는 호출 한 번으로 무한가지의 값을 출력할 수 있음.
  • % 다음으로 나오는 정보는 값이 어떤 규격으로 본래 형식(이진수)에서 출력되는 형식(문자) 변환되는지를 정함.
  • 변환 규격 %d : printf이 int값을 이진수로부터 10진수를 갖는 문자열로 변환되어 출력하는 규격을 정해줌.
  • 변환규격 %f : float 값에 대해 마찬가지로 규격을 정함.

 

- 서식 문자열의 일반적인 글자들은 생긴 그대로 출력될 것임. 변환 규격의 경우, 출력되어야 할 값들로 바뀌어져 출력됨.

int i;
int j;
float x;
float y;

i = 10;
j = 20;
x = 43.2892f;
y = 5527.0f;

printf("i = %d, j = %d, x = %f, y = %f\n", i, j, x, y);
  • printf 함수를 호출하면, 다음과 같은 결과가 출력됨. i = 10, j = 20, x = 43.289200, y = 5527.000000
  • 서식 문자열의 일반적인 글자들은 그냥 그대로 출력되었음.
  • 네 개의 변환 규격들은 순서대로 변수 i, j, x, y로 바뀌어져 출력됨.
[!!!] C 컴파일러들은 서식 문자열에서의 변환 규격의 개수와 출력되는 것들의 개수가 같은지를 반드시 확인하지 않는다. 다음과 같은 printf 함수의 호출을 보면 출력해야할 값보다 변환 규격이 더 많다.
printf("%d %d\n", i); /*** WRONG ***/
printf은 i의 값은 제대로 출력하고, 아무런 의미가 없는 두 번째 정수값을 출력할 것이다. 반대로 출력해야할 값이 변환 규격보다 많으면 비슷한 문제를 일으킨다.
printf("%d\n", i, j); /*** WRONG ***/
이 경우엔 printf이 i의 값은 출력하지만 j의 값은 출력하지 않을 것이다.
좀 더 구체적으로 언급하자면, 컴파일러들은 굳이 변환 규격이 출력되는 것의 유형에 적합한지를 확인하지 않는다. 만약 프로그래머가 잘못된 규격을 사용한다 할지라도, 프로그램은 실행이 될 것이고 아무런 의미가 없는 결과값을 출력할 것이다. 다음과 같이 int 변수 i와 float 변수 x가 틀린 순서로 되어있는 상태에서 printf을 호출해주었다고 하자.
printf("%f %d\n", i, x); /*** WRONG ***/
우선 printf은 서식 문자열이 지시하는 대로 따라야하기 때문에 지시한 대로 float 값과 int 값을 순서대로 출력할 것이다. 물론 두 개 다 의미없는 결과값일 것이다.

 

 

📌 변환 규격

- 변환 규격을 통해 프로그래머는 출력을 좀 더 다양하고 구체적으로 할 수 있음. 그렇기에, 변환 규격은 사용, 읽기 복잡함.

- 2단원에서 변환 규격을 통해 서식에 대한 정보를 첨가해줄 수 있다는 것을 배움.

- 구체적으로 보자면, float 값을 소수점 아래 한 자리까지만 출력해주기 위해 %.1f 를 이용함.

- 좀 더 일반적으로 보자면, 변환 규격은 %m.pX이나 %-m.pX와 같은 형식을 가짐. 여기서 m과 p는 정수 상수이고 X는 문자임. m과 p는 선택적으로 추가해주는 것이고, m과 p를 나누는 소수점은 p와 함께 사용해야함.

- 변환 규격 %10.2f에선 m은 10p는 2이고 X는 f임. 변환 규격 %10f에선 m은 10p는 사용되지 않았기 때문에 소수점과 함께 사용되지 않았으며, X는 f임. 반면에 변환 규격 %.2f의 경우 반대로 p는 2이고 m는 사용되지 않았음.

 

- m의 뜻은 최소 범위 너비(minimun field width)로, 출력할 글자의 최소 개수를 정함. 출력할 값의 글자가 m개보다 적다면, 주어진 범위 내에 있는 것임.

ex) %4d를 준 다음 출력할 값을 123으로 준다면, 실제 출력되는 값은 ·123일 것. (이 단원에선 스페이스를 ·로 표기함)

- 위의 경우와는 반대로 출력할 값의 글자가 m 개보다 크다면 자동으로 범위를 필요한만큼 넓힘. 그렇기 때문에 규격 %4d을 준 다음 출력할 값을 12345로 준다면, 실제 출력되는 값은 12345일 것.

- 만약 m앞에 마이너스 기호를 표기해준다면, 숫자를 왼쪽에 정렬하게 됨. 즉, 규격 %-4d을 준 다음 출력할 값을 123로 준다면, 실제 출력되는 값은 123·임.

 

- p의 뜻은 정확도임. 정확도는 x 혹은 규격자의 종류에 따라 다르게 정의됨. x는 값이 출력되기 전 어떤 방식의 변환이 적용되어야하는지를 결정함. 가장 자주 사용되는 숫자의 변환 규격자는 다음과 같음.

  • d - [Q&A] 정수를 십진법 형식으로 보여준다. p는 최소 자리수를 의미한다. 만약 필요하다면 숫자가 시작하는 부분에 추가적으로 0을 추가해준다. 만약 p가 포함되지 않았다면 p의 값이 1이라고 가정한다. 다시 말해 %d와 %.1d는 같은 것이다.
  • e - 소수를 지수 형식(물리량을 나타낼 때 사용하는 방식)으로 보여준다. p는 출력할 소수점 이하 자리수를 결정한다. 기본값은 여섯자리이다. 만약 p의 값이 0이라면 소수점을 출력하지 않는다.
  • f - 소수를 지수 없이 고정 소수점 형식으로 보여준다. p는 e 규격자와 같은 의미를 갖는다.
  • g - 소수를 숫자의 크기에 따라 지수 형식 혹은 고정 소수점 형식으로 보여준다. p는 최대 유효 숫자 자리수를 의미한다. 이는 소수점 아래 자리수를 의미하는 것이 아니다. f 변환과는 달리 g 변환은 추가적인 0을 출력하지 않는다. 심지어 출력할 값이 소수점 이하 자리수가 없다면 g는 소수점 조차 출력하지 않는다.

- g 규격자는 프로그램을 작성할 때 숫자가 사이즈를 예측할 수 없거나, 사이즈가 다양할 때, 이를 출력하는데 유용함. 적당히 크거나 적당히 작은 숫자를 출력할 때 고정 소수점 형식을 사용함. 허나 매우 크거나 매우 작은 숫자를 출력할 땐 지수 형식을 사용함으로써 숫자가 길지 않게 만듦.  %d%e%f%g 외에도 다른 다양한 규격자들이 있지만 이후 단원에서 차차 소개하도록 함.

 

 

🔥 [프로그래밍] printf를 통한 숫자 서식

🌐 tprintf.c

/* Prints int and float values in various formats */
#include <stdio.h>

int main(void)
{
  int i;
  float x;
  i = 40;
  x = 839.21f;

  printf("|%d|%5d|%-5d|%5.3d|\n", i, i, i, i);
  printf("|%10.3f|%10.3e|%-10g|\n", x, x, x);

  return 0;
}
  • printf의 서식 문자열 내부에 있는 |는 이 서식 문자열이 출력될 때 얼마나 많은 칸을 소모하는지를 보여주기 위해 사용함. %, \와는 달리 |는 printf에서 아무런 기능을 갖고있지 않아, 마음대로 사용할 수 있음.
  • 위 프로그램을 출력하면 다음과 같음.

|40|   40|40   |  040|

| 839.210| 8.392e+02|839.21 |

 

- 이 프로그램에서 사용한 변환 규격들에 대해 좀 더 구체적으로 살펴보자.

  • %d ― i를 십진수의 형태로 출력하되, 최소한의 공간만을 차지한다.
  • %5d ― i를 십진수의 형태로 출력하되, 최소 다섯 칸을 사용한다. i는 두 칸을 차지하므로, 추가적으로 빈 칸 세 칸이 더 추가되었다.
  • %-5d ― i를 십진수의 형태로 출력하되, 최소 다섯 칸을 사용한다. i는 두 칸을 차지하므로, 추가적으로 빈 칸 세 칸이 i가 먼저 출력되고 나서 추가되었다. 즉, i는 좌측으로 정렬된다.
  • %5.3d ― i를 십진수의 형태로 출력하되, 출력은 최소 다섯 칸, 숫자는 최소 세 칸을 사용한다. i는 두 칸 밖에 안되므로 세 칸을 채우기 위해 0이 추가되었다. 이제 세 칸이 찼으므로 부족한 두 칸을 빈 칸으로 채워 총 다섯칸을 채운다, i는 우측으로 정렬된다.
  • %10.3f ― x를 고정된 십진수의 형태로 출력하되, 출력은 최소 열 칸, 소수점 이하는 최소 세 칸을 사용한다. x는 소수점 이전 세 칸, 소수점 이후 세 칸, 소수점이 한 칸, 총 일곱 칸이 필요하므로 세 칸을 채우기 위해 x 좌측에 빈 칸 세 칸이 추가되었다.
  • %10.3e ― x를 지수형으로 출력하되, 출력은 최소 열 칸, 소수점 이하는 최소 세 칸을 사용한다. x는 지수를 포함하면 총 아홉 칸을 사용하기 때문에 x 좌측에 빈 칸 한 칸이 추가되었다.
  • %-10g ― x를 고정 십진수 형태나 지수형으로 출력하되, 출력은 최소 열 칸을 사용한다. 위의 경우에선 printf은 x를 고정 십진수 형태로 출력했다. 앞에 마이너스 기호는 x를 좌측으로 정렬하라는 의미이기 때문에 x 이후에 빈 칸 네 칸이 추가되었다.

💡 보통 언제 사용하나?

  • 값이 0-100 사이인 2차월 배열을 이중 포문으로 출력하면 40 3 39 39\n 3 100 3 35 이런식으로 나와서 보기 불편함. 이때, "%3d",a[i][j] 같은 방법으로 사용하면 깔끔하게 출력됨.

 

 

📌 확장 비트열

- \n과 같은 코드를 확장 비트열이라고 부름.

- 확장 비트열은 문자열로 하여금 컴파일러에서 문제를 일으킬 수도 있는 문자들을 출력하게 해줌. (프린트되지 않는 제어 문자들이나 "와 같이 컴파일러가 특별히 다루는 문자들을 의미함)

- printf의 서식 문자열 중에 가장 먼저 실행됨.

 

💡 몇 가지 확장 비트열

 

printf("Item\tUnit\tPurchase\n\tPrice\tDate\n");

 

이 프로그램을 실행하게 된다면 다음과 같이 나옴.

 

`Item Unit Purchase

Price Date`

 

- 위의 예시 말고도 자주 쓰이는 확장 비트열은 \"이고, 이는 " 문자를 출력함. "는 문자열의 시작, 끝을 나타내주는 문자이기에 문자열 내에선 확장 비트열을 사용하지 않는 이상 사용할 수 없음.

printf("\"Hello!\"");

 

위 구문을 실행한다면 다음과 같이 출력됨.

 

"Hello!"

 

- 당연하겠지만 문자열 내부에 \ 한 개를 사용할 수 없음. 컴파일러는 이를 확장 비트열의 시작으로 인식할 것이기 때문임. 즉, \ 문자열을 출력해주기 위해서는 두 번 연속해서 사용해주면 됨.

printf("\\"); /* 문자 \ 하나를 출력한다 */
printf("\\"); /* prints one \ character */

 

 

 

💡 본 내용은 K.N.King의 C Programming: A Modern Approach 책을 참고하였습니다.

'📚 STUDY > 🔥 C' 카테고리의 다른 글

Q&A  (3) 2024.09.02
[3-2] scanf 함수  (0) 2024.09.02
Q&A (GCC, GNU, 함수, 주석, 식별자, 레이아웃..)  (4) 2024.09.02
[2-8] C 프로그램의 레이아웃  (0) 2024.09.02
[2-7] 식별자  (0) 2024.09.02