뚜둔뚜둔✩

판다의 개발일지

C · C++

[C] printf 함수

2021. 12. 21. 23:29
    #include <stdio.h>
    
    int printf(const char *format, ...);

    printf 함수는 C에서의 표준 출력 함수로, 표준 출력 스트림인 stdout(standard output)에 정해진 format 형식의 문자열을 출력한다. formatconst 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 with
    a, 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 문자열을 어떻게 바꾸던 printfformat을 변경하지 않고도 형식을 그대로 유지할 수 있다.

    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]);
    }

    위의 예시를 보면 첫 번째 printfformat에서 %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