3-3-나.정수형의 길이

C의 정수형, 더 정확하게 말해서 컴퓨터가 표현하는 정수라는 개념은 수학에서 말하는 정수와는 의미가 약간 다르다. 수학의 정수는 음양으로 무한대의 값을 표현할 수 있지만 유한한 메모리를 가진 컴퓨터는 이런 무한한 값을 표현하지 못하며 자신에게 할당된 메모리 양만큼의 값만 기억할 수 있다. 그래서 가끔 연산 결과가 용량을 넘어서는 경우가 발생하기도 하는데 다음 예제를 실행해 보자.

 

: overflow

#include <Turboc.h>

 

void main()

{

     short a,b,c;

     unsigned short s,t,u;

 

     a=20000;

     b=30000;

     c=a+b;

     printf("%d+%d=%d\n",a,b,c);

 

     s=20000;

     t=30000;

     u=s-t;

     printf("%d-%d=%d\n",s,t,u);

}

 

정수형 변수로 간단한 덧셈, 뺄셈을 해 보았는데 실행 결과는 다음과 같다.

 

20000+30000=-15536

20000-30000=55536

 

세 개의 2바이트 정수(short) a, b, c를 선언하고 a에 20000, b에 30000을 대입한 후 이 두값을 + 연산자로 더해 c에 대입했다. 그러면 c는 당연히 50000이라는 값을 가져야겠지만 실제 결과는 엉뚱하게도 -15536으로 출력된다. 왜냐하면 a, b, c 변수는 부호있는 2바이트의 정수형인 short형으로 선언되었고 최대 32767이상의 수를 저장할 수 없기 때문이다. 50000이라는 값이 대입되기는 하지만 short형은 최상위 비트를 부호 비트로 해석하기 때문에 음수가 되어 버리는 것이다. 이런 식으로 변수의 저장 용량을 넘어서는 현상을 오버플로우(Overflow)라고 한다.

이런 문제가 발생한 근본적인 원인은 만단위의 수치를 저장하는데 short형을 사용했다는데 있다. a,b,c를 unsigned short형으로만 바꾸어도 위 예제는 제대로 실행된다. 그러나 그렇게 하더라도 65535이상의 수를 저장할 수는 없다. 더 큰 수를 다루려면 int나 unsigned같은 4바이트의 더 큰 타입을 사용해야 한다. int는 20억 정도의 큰 수치를 저장할 수 있으므로 일반적으로 오버플로우 걱정을 하지 않아도 된다.

변수의 표현 범위를 초과하는 현상과 반대로 최소 표현수에 미치지 못하는 경우도 발생할 수 있다. s, t, u는 모두 부호를 표현하지 못하는 unsigned short로 선언되었으며 20000이라는 값을 가지는 s에서 30000이라는 값을 가지는 t를 빼서 u에 대입했다. u에 대입되는 값은 -10000이 아니라 55536이라는 양수값이 되어 버린다. unsigned short형이 표현할 수 있는 최소수는 0인데 이 값보다 더 작은 값을 대입했으므로 계산 결과가 틀려지는 것이다.

수학적인 연산을 할 때는 항상 이 점을 주의해야 한다. 아주 간단할 것 같은 연산도 정확한 타입과 함께 사용해야만 결과가 제대로 나온다. 메모리가 지극히 부족한 상황이 아닌 한은 정수가 필요할 때 부호 있는 4바이트 정수인 int를 사용하면 별 문제가 없다. int는 음양으로 20억이라는 실생활에서 거의 부족하지 않는 정도의 표현 범위를 가지고 있기 때문이다.

정수형 타입의 도표를 보면 int와 long은 크기나 부호 여부가 동일하며 따라서 표현할 수 있는 수의 범위도 완전히 동일하다. 왜 똑같은 타입을 둘 씩이나 정의해 놓았는지 의아하겠지만 이 둘은 엄밀하게 말하면 다른 타입이다. 아니, 다른 타입이라기 보다는 달라질 수 있는 타입이라고 하는 편이 옳을 것 같다.

C 언어의 타입 정의에 int 형은 "CPU의 레지스터와 동일한 크기를 가지는 타입"으로 정의되어 있다. 레지스터란 CPU내의 임시 기억 장소이며 레지스터의 크기에 따라 CPU의 비트 수를 정의한다. 즉, 레지스터가 16비트이면 16비트 컴퓨터, 32비트이면 32비트 컴퓨터라고 부른다. 비트 수가 높으면 높을수록 CPU가 한 번에 처리할 수 있는 자료양이 많아지므로 더 성능이 높다고 할 수 있다.

, int형은 CPU가 가장 효율적으로 다룰 수 있는 정수형으로 정의되어 있으며 그래서 int형의 실제 크기는 플랫폼에 따라 달라진다. 다음에 알아볼 포인터형도 마찬가지이다. 과거 8086이나 80286같은 16비트 CPU 시절, 윈도우즈 3.1같은 16비트 운영체제에서 int는 16비트였었다. 그러나 386이후의 CPU와 윈도우즈 95이후의 32비트 운영체제에서 int는 32비트이다. 64비트 CPU가 나오면(이미 나와 있다) 그때는 int형이 64비트(8바이트)가 될 것이다.

반면 long형은 그 크기가 4바이트로 고정되어 있어 어떤 플랫폼에서나 4바이트이다. int와 long이 동일한 크기를 가지는 것은 32비트 플랫폼에서 뿐이며 16비트에서는 서로 다른 타입이고 64비트에서도 달라질 것이다. 꼭 4바이트를 쓰고 싶으면 long형으로 선언하고 플랫폼의 환경에 따라 적절한 크기를 자동으로 선택하고 싶다면 int형으로 선언하면 된다.

최근 64비트 CPU가 발표되고 점점 더 큰 수를 다룰 일들이 많아지면서부터 C언어도 64비트의 정수를 지원하기 시작했다. 비주얼 C++과 Dev-C++은 __int64라는 타입을 지원하며 이 타입을 사용하면 무려 1800경(264)이라는 엄청난 수를 표현할 수 있다. 다음은 64비트 정수를 사용하여 억단위의 정수끼리 곱해본 것이다.

 

: int64

#include <Turboc.h>

 

void main()

{

     __int64 a,b,c;

 

     a=111111111;

     b=111111111;

     c=a*b;

     printf("%I64d\n",c);

}

 

흔히 전자 계산기를 테스트하기 위해 일련의 1을 곱해보는데 12345678987654321이라는 결과가 나오면 제대로 동작하는 것이다. printf로 64비트 정수를 출력하려면 %I64d라는 서식을 사용한다.