본문 바로가기

IT

[algospot/알고리즘] 알고스팟 '왕초보' 난이도 CONVERT 풀기(C99 Default argument promotion)

단위를 환산하는 문제는 매우 쉬운 편에 속합니다.

초등학교때 배운 비례식이 결국 단위환산이겠지요.


- 단위를 제대로 파싱하는지

- 그리고 자릿수가 제대로 출력하는지

위에서 실수만 하지 않는다면 정답을 도출할 수 있을겁니다.



- 제목 : CONVERT

- 난도 : ☆☆☆☆☆ (0점/5점)

- 문제 : 단위를 변환하세요.

                 킬로그램 : 파운드 = 0.4536 : 1.0000 =   1.0000 : 2.2046

                 볼륨 : 갤런 = 1.0000 : 0.2642 = 3.7854 : 1.0000   

- 답안

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    register int n = 0;
    int count = 0;

    scanf("%d", &count);

    for (; n < count; n++) {
        double number = 0.0f;
        char unit_old[4] = {0, };
        char *unit_new = NULL;

        scanf("%lf %s", &number, unit_old);

        if (!strcmp(unit_old, "kg")) {
            number *= 2.2046;
            unit_new = "lb";
        } else if (!strcmp(unit_old, "lb")) {
            number *= 0.4536;
            unit_new = "kg";
        } else if (!strcmp(unit_old, "l")) {
            number *= 0.2642;
            unit_new = "g";
        } else {
            number *= 3.7854;
            unit_new = "l";
        }

        printf("%d %.4lf %s\n", n + 1, number, unit_new);
    }

    return 0;
}


- scanf()에서 double 타입은 %lf로 받아야합니다.

  f는 float이지만, d는 이미 integer가 사용하고 있기 때문에,

 double에게는 다른 문자를 할당해야했지요.

  f 앞에 l을 붙여서 long float의 약어인 %lf를 사용합니다.


- printf()에서 double 타입은 %lf로 받습니다.

  하지만, %lf 대신 %f를 사용해도 동작에는 차이가 없습니다.

  그래서 굳이 %f로 사용하는 사람들도 있습니다.

  왜 그럴까요?우선 prototype부터 알아보시죠.


- Prototype이란?

  함수에서 prototype이란 함수의 argument를 의미합니다.

  함수를 선언&정의할 때 prototype을 명시할 수도 있고 그렇지 않을 수도 있습니다.


- Prototype을 명시한 경우

/* prototype과 선언하는 경우 */
void func(char a, short b, float c);

/* prototype과 정의하는 경우 */
void func(char a, short b, float c)
{ /* ... */ }


- Prototype을 명시하지 않는 경우

/* prototype 없이 선언하는 경우 */
void func();

/* prototype 없이 정의하는 경우 : K&R의 "오래된" C Style */
void func(a, b, c)
    char a;
    short b;
    float c;
{ /* ... */ }

/* 가변인수를 사용하는 경우 */
void func(int a, ...);


- Prototype이 명시되어 있는 함수에 argument를 넘겨주는 것은 아무런 문제가 없습니다.

 char는 char형으로, short는 short형으로, float는 float형으로 전달됩니다.

 반면, prototype이 명시되어 있지 않는 함수에서 argument는 그대로 넘겨주지 않습니다.

 char, short는 integer로 자동변환되고, float는 double로 자동변환됩니다(아래 C99 규약).

If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.

  unsigned char와 unsigned short는 unsigned int로 자동변환되겠죠.


- printf()는 가변인자를 사용하고 있기 때문에 prototype이 명시되어있지 않은 함수입니다.

  따라서 printf()에 인자로 float를 넣어주면 자동변환에 의해 double로 바뀝니다.

  printf() 내부에서는 float에 대한 처리를 할 필요가 없게 됩니다.

  무조건 double 형이 들어오기 때문이지요.

  따라서 "%f"나 "%lf"나 똑같은 취급을 받습니다.

  물론 개인적으로는 가독성을 위해 "%lf"를 사용하는게 낫다고 생각합니다.


- 그렇다면, 왜 default argument promotion을 하는 것일까요?

  1988년 이전의 "dark age" 시절에는 함수 prototype이 없었습니다.

  그렇기 때문에 컴파일러 입장에서는,

  함수 아규먼트로 char나 short를 입력해도 함수정의의 타입인지 확신할 수 없었습니다.

  그래서 레지스터(4바이트)에 넣을만한 크기로 각각의 인자를 promotion해서 전달하였습니다.

  1바이트 char나 2바이트 short를 4바이트 int로 promotion한다고 추가 비용이 없습니다.

  어차피 4바이트 단위로 데이터가 관리되기 때문이지요.

  이렇게 넘겨받은 변수는 정의된 함수에서 캐스팅하여 사용했습니다.

  

- 개인적인 생각입니다만 컴파일한 파일을 메모리에 올려서 사용할 때,

  함수를 사용하는 쪽에서 float 타입의 0010 0001을 넘겨주었지만,

  함수정의에서 double로 패러미터를 정의했다면,

  0000 0000 0010 0001처럼 앞에 4바이트가 더 붙어야합니다.

  함수를 사용하는 쪽에서 미리 공간을 마련하여 스택에 쌓지 않으면,

  받는 쪽에서 캐스팅하여 사용할때 공간을 추가해야하니 고려해야할게 많았겠지요.

  그래서 관리의 용이함을 위해,

  미리 double만큼의 공간을 잡고 거기서 float를 사용하는게 쉽지 않았을까 싶기도 합니다.

  (물론 지금 설명은 dark age 기준의 설명입니다. 현대의 컴파일러 기준이 아닙니다.)


- 이상으로 printf()에서 %lf를 사용한 이유를 설명하였습니다.


- printf()에서 소수점 자릿수를 표현하려면 ".n"(여기서 n은 숫자)를 사용합니다.

  문제에서는 소수점 4자리로 출력을 해야했기 때문에 "%.4f"를 사용했습니다.

  ".4"를 사용하면 소수점이 없더라도 ".0000"이 붙게 됩니다.


오늘을 알고리즘 자체보다는,

"%.4f"를 설명하기 위해 더 공을 드린 것 같습니다. :)


이로써 알고스팟에서 총 8문제를 풀었습니다.

순위는 1617위가 되었습니다.


그럼 좋은 하루 보내세요.

끝_