OneDev

[C] 8. 오버플로우(overflow) 본문

Language/C

[C] 8. 오버플로우(overflow)

one_dev 2022. 7. 15. 09:49

1. 서론

우리는 지금까지 변수를 이용해 여러가지 연산을 수행하는 방법에 대해 학습하였다.

숫자와 연산자만 이용해 모든 연산을 할 수 있다면 참 편하고 좋으련만

애석하게도 C에서는 연산을 할 때 제약이 있다고한다.

그 이유는 변수의 타입 마다 보관할 수 있는 데이터의 크기가 정해져있기 때문이다.

 

예를 들면 int형 변수는 -2147483648 부터 2147483647 까지의 정수 데이터를 보관할 수 있다.

 

그렇다면, 보관할 수 있는 크기를 넘어가는 데이터를 변수에 저장하고자 하면 무슨 일이 발생할까?

 

앞서 int 형 변수가 21447483647 까지의 정수 데이터를 보관할 수 있다 했으므로, 다음과 같이 코드를 작성한 뒤 실행시켜보자.

#include <stdio.h>

int main() {
	int a = 2147483647;         	// 정수형 변수 최대값을 입력
	printf("a : %d \n", a);

	a++;					// 최대값에서 1을 더한뒤
	printf("a : %d \n", a);			// 출력하면 무슨일이 발생할까?
	

	return 0;

}

위의 코드를 실행해 보면 아래와 같은 결과가 나온다.

 

놀랍게도 음수가 나온것을 볼 수있다.

엄청나게 큰 양수에 1을 더했는데 엄청나게 작은 음수가 나오다니 참으로 이상한 일이다.

이를 이해하기 위해서는 컴퓨터에서 어떻게 음수를 표현하는지 알 필요가 있다.

 

 

2. 2의 보수 (2's complement) 표현법

컴퓨터에서 음수를 어떻게 표현할 지에 대해 여러가지 아이디어가 있었지만, 결국 사용되는 것은 2의 보수 표현법이란 것만 알아두자. 이제 2의 보수 표현법이란 무엇인지 알아보자.

 

먼저 컴퓨터에서 왜 2의 보수 표현법을 채택하게 되었는지 알아보자.

2의 보수 표현법은 다음과 같은 장점이 있기 때문이다.

 

  • 음수나 양수 사이 덧셈 시에 굳이 부호를 고려하지 않고 덧셈을 수행해도 된다.
  • 맨 앞 비트를 사용해서 부호를 빠르게 알아낼 수 있다

이제 구체적으로 2의 보수 표현법에 대해 알아보도록 하자.

 

먼저 어떤 수 a 에 -a 를 더하면 0이 된다는 사실을 떠올려보자.

예를 들면 7에 -7을 더하면 0이 되는 것처럼 말이다.

 

이제 이진수 체계에서 생각해보자.

7을 이진수로 나타내면 0111이다.

이진수 0111에 무엇을 더해야 0000이 나올까?

 

계산상의 편의를 위해,  이 덧셈을 수행할 때 컴퓨터는 4비트만 기억한다고 가정해보자.

그렇다면 -7 의 이진수 표현으로 가장 적당한 수는 바로 1001 이 될 것이다.

0111  1001 을 더하면 10000 이 되는데, 컴퓨터가 4 비트만 기억하므로 맨 앞의 1 은 버려져 그냥 0000 이 되기 때문이다.

 

이렇게 덧셈을 고려하였을 때 가장 자연스러운 방법으로 음수를 표현하는 방식을 바로 2 의 보수 표현이라고 한다.

2 의 보수 표현 체계 하에서 어떤 수의 부호를 바꾸려면 먼저 비트를 반전 시킨 뒤에 1 을 더하면 된다.

위의 예시에서 보면 -7을 나타내기 위해 0111을 반전시키고 1000이 된 것에 1을 더해 1001 으로 나타내었다.

이 체계에서 중요한 점은 0000 의 2의 보수는 그대로 0000이라는 점이다.0000을 반전시키면 1111이 되는데 여기에 다시 1을 더하면 0이 되기 때문이다.

 

또한 어떤 수가 양수인지 음수인지 판별하는 것 또한 어렵지 않다.맨 앞의 수를 부호비트라 보면 되기 때문이다.구체적으로는 맨 앞의 수가 1이면 음수, 0이면 양수로 보면 된다.예를 들면, 1101은 맨 앞의 수가 1이기 때문에 음수이다.이 수가 무슨 수인지 알고 싶다면 보수를 구한 뒤에 - 만 붙여주면 된다.1101을 반전시키면 0010이고 여기에 1을 더하면 0011이다. 이는 십진법으로 표기했을 때 3이므로,1101은 -3 임을 알 수 있다.

 

한 가지 재미있는 점은 2 의 보수 표현법에서 음수를 한 개더 표현할 수 있다는 것이다.

왜냐하면 1000 의 경우 음수 이지만 변환 시켜도 다시 1000 이 나오기 때문이다(1000 --> 0111 --> 1000).

실제로 int 의 범위를 살펴보면 -2,147,483,648 부터 2,147,483,647 까지 인데 음수가 1 개 더 많은걸 볼 수 있다.

 

3. 오버플로우(overflow)

다시 처음으로 돌아가서 코드를 살펴보자.

#include <stdio.h>

int main() {
	int a = 2147483647;         	
	printf("a : %d \n", a);

	a++;					
	printf("a : %d \n", a);			

	return 0;

}

우리는 처음에 a에 int형 자료가 가질수 있는 최대값을 저장해주었다.

아마 a에는 0x7FFFFFFF = 0111.......11111 이 저장되었을 것이다(이진수로 표현할 수 있는 최대값).

 

우리는 상식적으로  a 의 현재 값이 int 가 보관할 수 있는 최대값이니

1을 더 증가 시킨다면 오류가 나게 하거나 아니면 그냥 현재 값 그대로 유지하게 하고 싶다.

하지만 CPU 는 그냥 0x7FFFFFFF 값을 1 증가 시킨다.

따라서 a++ 이후에 a 에는 0x80000000 (이진수로 1000 0000 ... 0000) 이 들어가게 되는 것이다.

문제는 0x80000000 을 2의 보수 표현법 체계하에서 해석하면 (0111 1111 ... 1111) 이 되고

다시 1 을 더하면 (1000 0000 ... 0000) 이 되므로 -0x80000000, 즉 -2147483648 이 된다.

따라서 위와 같이 양수에 1 을 더했더니 음수가 나와버리는 불상사게 생기게 되는 것이다.

이와 같이 자료형의 최대 범위보다 큰 수를 대입하므로써 발생하는 문제를 오버플로우(overflow) 라고 하며,

C 언어 차원에서 오버플로우가 발생하였다는 사실을 알려주는 방법은 없기 때문에  스스로  사용하는 자료형의 크기를 신경 써줘야만 한다.

Comments