Link

형 변환

자료형(type)이 다른 대상에 연산을 하면 대상의 형 변환이 일어나는데, 형 변환에는 몇 가지 규칙이 있다. 일반적으로 자동변환이 일어나는 것은 f + i에서와 같이 정보의 손실 없이 변환 가능할 때뿐이다. 부동소수를 배열의 첨자(index)로 사용하는 것과 같이 상식에서 벗어나는 것은 허용되지 않는다. 긴 정수를 짧은 정수로, 혹은 부동소수형을 정수로 변환하는 수식은 컴파일할 때 경고(warning)이 나오겠지만 불가능한 것은 아니다.

문자는 하나의 정수일 뿐이므로 산술식에서 자유롭게 쓸 수 있다. 이것은 문자 변환에 상당한 융통성을 부여한다. 문자열 속에 숫자가 있을 때 그것을 정수로 바꾸어 주는 함수 atoi를 통해 이를 실험해 볼 수 있다.

int atoi(char s[])
{
    int i, n;

    n = 0;
    for (i = 0; s[i] >= '0' && s[i] <= '9'; ++i)
        n = 10 * n + (s[i] - '0');
    return n;
}

문자를 정수로 변환하는 데는 한 가지 불명확한 점이 있는데, 문자형 변수가 부호형(signed)인지 비부호형(unsigned)인지가 기계마다 다르다는 것이다. 어떤 기계에서 가장 왼쪽 비트가 1인 문자는 음의 정수(“부호확장”)로 변환된다. 물론 왼쪽 비트가 1인 경우도 양수로 취급하는 기계도 있다.

C의 정의에 의하면 기계의 표준 출력문자 세트의 어떤 글자도 음수가 되지 않는다. 그러나 문자 변수에 저장된 임의의 비트 패턴이 어떤 기계에서는 음수로 나타날 수도 있고, 어떤 기계에서는 양수로 나타날 수도 있다. 문자 변수에 문자가 아닌 숫자가 저장되는 것을 막기 위해서는 프로그램에서 부호형인지 비부호형인지를 확인해 주어야 한다.

i > j같은 관계 수식이나 && 혹은 ||로 연결된 논리 수식은 참일 때 1, 거짓을 때 0의 값을 갖는다. 따라서 다음의 대입문은 c가 숫자일 때 1, 그 외의 경우에는 0의 값이 된다.

d = c >= '0' && c <= '9'

그러나 isdigit같은 함수는 참일 때 0도 1도 아닌 수를 반환해 줄 수도 있다. if, while, for 등의 검사 부분에서도 마찬가지로 “참”이란 “0이 아님”을 의미한다.


암시적 형변환

이항 연산자에서 피연산자의 자료형이 서로 다를 경우에는 계산을 진행하기 전에 작은 쪽에서 큰 쪽으로 변환된다.
부호 없는 피연산자가 없다면 다음과 같은 간단한 법칙으로 충분하다.

  1. long double이 있으면 둘 다 long double로 변환한다.
  2. 그렇지 않고 double이 있으면 둘 다 double로 변환한다.
  3. 그렇지 않고 float가 있으면 둘 다 float로 변환한다.
  4. 그렇지 않으면 char 또는 short를 int로 변환한다.
  5. 그리고 long인 피연산자가 있으면 모두 long으로 변환한다.

한 가지 주의해야 할 것은 수식 중의 float는 자동으로 double로 변환되지 않는다는 것이다. 일반적으로 <math.h>에 선언되어 있는 함수는 배정도를 사용한다. 큰 배열에서는 기억장소를 절약하기 위해 float를 쓰고, 또 기억장소가 충분하다고 하더라도, 계산시간을 절약하기 위해 사용할 때도 있다.

참고로 오늘날 컴퓨터 하드웨어들은 배정도를 기본으로 쓰기 때문에 double이 더 빠른 경우가 많다.

부호 없는 피연산자가 개입된 경우의 변환 법칙은 더욱 복잡하다. 문제는 부호형과 비부호형의 비교 결과는 기종의 정수형의 크기에 따라 다르다는 것이다. 예를 들어 int가 16비트이고 long형이 32비트라 하자. 다른 자료형으로 저장된 수를 비교할 때 작은 범위의 것이 큰 범위의 것으로 변환되므로 -1L < 1U이 된다. 이 경우는 -1L이 unsigned long 형태로 변환되어 큰 양의 정수처럼 보이게 되기 때문이다.
변환은 대입문에서 나타나는데, 우변은 좌변과 같은 자료형으로 변환되어 그것이 결과의 자료형이 된다. 문자는 부호확장(sign extension)이든 아니든 정수로 변환된다. 긴 정수는 상위 비트를 버림으로써 짧은 정수 혹은 문자로 변환된다. 그러므로 다음과 같은 경우의 c는 값이 변하지 않는다.

int i;
char c;
...
i = c;
c = i;

이것은 부호 확장에 관계없이 성립한다. 그러나 대입 순서를 바꾸면 정보를 잃을 수 있다. 만약 x가 float이고 i가 int라면 x=i와 i=x에서 모두 변환이 일어나는데, float를 int로 변환하면 소수 부분을 절단한다. double이 float로 변환될 때 소수 이하를 버릴지 반올림할지는 기종에 따라 다르다.

함수 호출에 의해 매개변수가 함수로 전달될 때에도 형 변환이 일어난다. 함수 원형(prototype)이 없으면 char, short는 int로 변환되고 float는 double로 된다. 이것이 바로 함수가 char이나 float로 호출할 때에도 함수 매개변수들을 int와 double로 지금가지 선언해 온 이유이다.


강제 형변환(cast)

(type)expression

수식은 위의 규칙에 의하여 지정된 자료형으로 변환된다. 예를 들어, sqrt라는 라이브러리 루틴은 double 인수를 필요로 하고, 그렇지 않으면 예상치 못한 결과를 낳는다. 따라서 n이 정수라면 sqrt에 넘겨주기 이전에 다음과 같이 double로 변환하여 사용할 수 있다.

sqrt((double)n);

캐스트는 n의 값을 적절한 자료형으로 변환하고 n 자체는 변하지 않는다는 것에 주의해야 한다.

매개변수들은 보통 함수 원형(function prototype)으로 선언되어야 하고, 함수호출이 일어날 때는 이 선언으로 인해 자동적으로 변환이 일어난다. 그러므로 다음과 같이 함수원형에서 주어진 sqrt에서

double sqrt(double);

다음과 같은 호출의

root2 = sqrt(2);

정수 값 2는 캐스트를 사용하지 않아도 double 형 변수 2.0으로 강제 변환된다.

함수 원형은 컴파일러에게 정보를 주는 것이다. 이 정보에 따라 컴파일러는 강제 형 변환을 수행한다.