들어가기 전에

컴퓨터는 우리가 작성한 프로그램을 구동하기 위해 다양한 물리적 장치를 사용한다. 그 중 하나는 메모리로, 프로그램이 필요한 정보가 저장되는 곳이다. 메모리의 용량은 무한하지 않기 때문에, 때때로 프로그램에서 우리가 의도하지 않은 오류가 발생하기도 한다.

#메모리 #오버플로우

컴퓨터는 RAM(랜덤 액세스 메모리)이라는 물리적 저장장치를 포함하고 있다. 우리가 작성한 프로그램은 구동 중에 RAM에 저장되는데, RAM은 유한한 크기의 비트만 저장할 수 있기 때문에 때때로 부정확한 결과를 내기도 한다.

부동 소수점 부정확성

아래와 같이 실수 x, y를 인자로 받아 x 나누기 y를 하는 프로그램이 있다고 가정해보자.

#include <cs50.h>
#include <stdio.h>

int main(void)
{
	// 사용자에게 x 값 받기
	float x = get_float("x: ");
	// 사용자에게 y 값 받기
	float y = get_float("y: ");
	// 나눈셈 후 출력
	printf("x/y = %.50f\\n", x / y);
}

나눈 결과를 소수점 50자리까지 출력하기로 하고, x에 1을, y에 10을 입력하면 아래와 같은 결과가 나온다.

x: 1
y: 10
x / y = 0.10000000149011611938476562500000000000000000000000

정확한 결과는 0.1이 되어야 하지만, float 에서 저장 가능한 비트 수가 유한하기 때문에 다소 부정확한 결과를 내게 되는 것이다.

정수 오버플로우

비슷한 오류로, 1부터 시작하여 2를 계속해서 곱하여 출력하는 아래와 같은 프로그램이 있다고 해보자.

#include <stdio.h>
#include <unistd.h>

int main(void)
{
	for(int i = 1; ; i *= 2)
	{
		printf("%i\\n", i);
		sleep(1);
	}
}

우리가 변수 i를 int로 저장하기 때문에, 2를 계속 곱하다가 int 타입이 저장할 수 있는 수를 넘은 이후에는 아래와 같은 에러와 함께 0이 출력될 것이다.

...
1073741824
overflow.c:6:25: runtime error: signed integer overflow: 1073741824 * 2 cannot be represented in type 'int'
-2147483648
0
0
...

정수를 계속 키우는 프로그램에서 10억을 넘기자 앞으로 넘어갈 1의 자리가 없어진 것이다. int에서는 32개의 비트가 다였기 때문이다. 그 이상의 숫자는 저장할 수 없는 것이다. 이것이 바로 정수 오버플로우이다.

이런 오버프로우 문제는 실생활에서도 종종 발견된다.

1999년에 큰 이슈가 되었던 Y2K문제는 연도를 마지막 두 자리수로 저장했던 관습때문에 새해가 오면 '99'에서 '00'으로 정수 오버플로우가 ㅂ라생하고, 새해가 2000년이 아닌 1900년으로 인식되는 문제였다. 그리고 세계는 수백만 달러를 투자해 프로그래머들에게 더 많은 메모리를 활용하여 이를 해결하도록 했다. 이는 통찰력 부족으로 발생한 아주 현실적이고 값비싼 문제였다.

또 따른 사례로 비행 보잉 787에서 구동 후 248일이 지나면 모든 전력을 잃는 문제가 있었다. 왜냐면 강제로 안전모드로 진입했기 때문이다. 이는 소프트웨어의 변수가 248일이 지난 뒤에 오버플로우가 되어 발생하였기 때문이였다. 248일을 1/100초로 계산하면 대략 2의 32제곱이 나온다. 보잉을 설계할 때 사용한 변수보다 너무 커졌던 것이다. 이를 해결하기 위해 주기적으로 재가동을 하여 변수를 다시 0으로 리셋했다.