C 언어(3) - 자료형 더 깊게 이해하기(이진법)

이진법 이해, 정수 자료형 심화 이해

Featured image

🔚 짧게 하는 복습

✅ 1. C언어에서 자료형은 정수, 실수, 문자 3가지가 있다.

✅ 2. 변수란 값을 저장하는 곳이며, 선언하는 방법은 자료형 변수이름 = 변수에 넣을 값이다.

✅ 3. printf는 %를 이용해서 formatting 할 수 있다.

✅ 4. \n를 문장 중간에 입력하면 줄 바꿈을 할 수 있다.

혹시 기억이 안 난다면, 다시 돌아가자


저번 수업을 착실히 들었다면, 자료형에 3가지 형태가 있다는 것을 기억할 것이다.

실제로 C언어에서 정수형, 실수형, 문자 총 3가지 밖에 없다.

그런데 아래 코드를 적어서 실행시켜보자.

엥 우리가 적은 값은 2147483648이라는 큰 숫자인데, 출력값은 -2147483648이라는 엄청 작은 숫자가 나온다.

분명 2147483648은 정수형이 맞고, 변수 선언부터 출력까지 문제가 없다. 무엇이 잘못된 걸까?


이진법이란?

자료형에 대해 깊은 이야기를 하기 전에, 이번 강의에서는 이진법을 다루겠다.

뜬금없이 갑자기 왜 이진법이냐고 생각할 텐데, 우리가 숫자를 표현하는 법은 십진법이고 컴퓨터가 숫자를 표현하는 법은 이진법으로 다르기 때문이다.

이진법은 2가지 숫자, 0과 1로만 숫자를 표현하는 방법이다. 또 이렇게 표현된 숫자를 이진수라고 한다.

즉 101, 111101, 101001 등이 이진법으로 표현된 숫자로 볼 수 있다.

십진법과 구분하기 위해 아래 첨자로 1012, 1111012, 1010012로 표현한다.

이진법은 소수점 바로 윗자리부터 왼쪽으로 20, 21, 22…자리, 소수점 바로 아랫자리부터 오른쪽으로, 2-1, 2-2, 2-3… 자리라고 한다.

그림1

즉 이렇게 자릿값을 가진다는 것이다.

이진법을 우리가 익숙한 십진법으로 바꾸려면, 각 자리의 숫자와 자릿값을 모두 곱해서 더하면 된다.

예를 들어 위 숫자는 아래처럼 구할 수 있다.

00010.1102

= 0 x 24 + 0 x 23 + 0 x 22 + 1 x 21 + 0 x 20 + 1 x 2-1 + 1 x 2-2 + 0 x 2-3

= 1 x 21 + 1 x 2-1 + 1 x 2-2

= 2 + 0.5 + 0.25

= 2.75

또한, 1 ~ 31까지를 테이블로 만들어서 확인해보면

그림2

이렇게 확인할 수 있다.


부호 절대값 방식

그런데, 위의 이진법은 -42와 같은 음수를 표현할 방법이 없다.

그래서 컴퓨터 공학자들은 음수로 이진법을 표현하는 방법을 떠올린다.

이는 의외로 굉장히 간단한데, 맨 앞에 숫자 하나를 더 붙여서 0은 양수, 1은 음수로 간주하는 것이다.

즉 1112은 7이라는 숫자인데, 여기서 01112은 +7, 11112은 -7이라는 숫자로 표현하겠다는 것이다.

이 방법을 부호 절대값 방식이라고 한다.

하지만 이 방식 역시 오래가지 못한다. 생각보다 단순한 문제에 봉착하기 때문이다.

바로 0을 표현할 때 +0과 -0으로 두 개가 나타난다는 점이다.

컴퓨터는 0과 어떤 값을 비교하는 일이 매우 많아서, 이는 생각보다 비효율적인 방법이 된다.


1의 보수법

위의 문제를 해결하기 위해 첫 번째 해결 방법은 1의 보수법이다.

여전히 가장 앞의 숫자는 부호를 나타내는 숫자인데, 1의 보수법은 양수의 2진수를 모두 반전시켜서 음수로 표현하는 방법이다.

여기서 반전이라는 것은 각 자리의 0은 1로, 1은 0으로 바꾸는 것이다.

즉, 01012이 +5라는 숫자인데, 10102은 -5를 나타낸다는 것이다.

이는 위의 부호 절대값 방식보다 계산이 편하다는 장점이 있다.

하지만 여전히 +0, -0의 문제를 해결할 수 없다.


2의 보수법

위의 문제를 해결하기 위해 2의 보수법을 만들어낸다.

여전히 가장 앞의 숫자는 부호를 나타내는 숫자이다.

2의 보수법은 양수의 2진수를 모두 반전시키고 1을 더하는 방법이다.

즉, 01012이 +5라는 숫자인데, 반전시킨 10102이 아니라 1을 더한 10112이 -5를 나타낸다는 것이다.

이렇게 하면, 아래 그림처럼 드디어 0을 하나로 만들며 음수와 양수를 쉽게 표현할 수 있게 된다.

그림3

또한, 위의 그림에서 유추할 수 있듯이 음수, 양수를 모두 표현하는 n 자리의 정수 이진수는, 2n개의 숫자를 표현할 수 있다.

그리고 -2n-1 ~ 2n-1-1까지 범위의 숫자를 표현할 수 있다.


int가 숫자를 저장하는 방법

아래의 코드를 따라 적고 실행해보자.

위의 코드에서는 새로운 함수가 나오는데

sizeof()

이라는 함수이다.

sizeof()는 ()안에 들어오는 자료형이나 변수가 얼마나 큰 저장공간을 가지는지를 반환한다.

단위는 바이트로, 1바이트는 8bit이다.

비트는 한 자리의 이진수, 즉 0 또는 1을 넣을 수 있는 저장공간을 의미한다.

이 말은 int는 32bit를 가진다는 의미이고, 음수와 양수가 다 가능한 32자리의 이진수를 표현할 수 있다는 뜻이다.

위에서 배운 것처럼,

또한, 위의 그림에서 유추할 수 있듯이 음수와 양수를 모두 표현하는 n 자리의 정수 이진수는, 2n개의 숫자를 표현할 수 있다.

그리고 -2n-1 ~ 2n-1-1까지 범위의 숫자를 표현할 수 있다.

을 이용하면 int

232개의 숫자를 표현할 수 있다.

그리고 -231 ~ 231-1까지 범위의 숫자를 표현할 수 있다.


이진법에서 오는 int의 한계

자 우리가 처음 작성했던 코드의 숫자를 보자.

2147483648이 숫자는 사실 231의 값을 가지는 숫자이다.

어떤가? 이제는 왜 이상한 숫자가 나오는지 알겠는가?

int의 최댓값은 231-1인데 231을 변수에 저장하려 했으니 잘못된 값이 저장된 것이다.

최댓값보다 큰 숫자를 담으려 해서 생기는 문제를 오버플로우, 최솟값보다 작은 값을 담으려 하면 생기는 문제를 언더플로우라고 한다.

그렇다면 2147483648을 표현할 방법은 아예 없을까?

그렇지 않다. 컴퓨터 공학자들은 너무 작은 숫자를 많은 저장공간에 할당해서 저장공간을 낭비하거나, 큰 숫자를 다루고 싶은데 저장공간이 모자라는 것을 막기 위해 다양한 자료형을 만들었다.


다양한 정수 자료형

그림 4

우선은 정수형에 관해서만 이야기할 예정이기에, int로 끝나는 자료형들만 확인하자.

우선은 크게 short int, int, long int, long long int로 나누어진다.

short int는 16bit, int는 32bit(예전에는 16비트 시스템에서는 16비트로 쓰였지만, 현재는 거의 없다), long int는 32bit, long long int는 64bit의 저장공간을 가진다.

그다음은 signed와 unsigned로 나뉘는데 signed는 부호가 있는 숫자를 저장할 때, unsigned는 부호가 없는 양수만을 저장할 때 쓰인다.

unsigned는 음수를 표기할 필요가 없으므로 n 비트라면 0 ~ 2n-1의 숫자를 저장할 수 있다.

그래서 잘 보면, 현재 64bit 시스템을 기준으로

int와 long int는 사실상 같고, 앞에 붙은 signed는 생략할 수 있다.

또한, 추가로 long long int나 short int 뒤에 있는 int 역시 생략할 수 있다.

오늘 배운 내용은 정말 어렵다. 꼭 곱씹고 이해하고 넘어가길 바란다.


📖 오늘의 핵심(다 알기 전까지는 넘어가지 말자❗)

✅ 1. C언어에서 정수형은 (unsigned) short int, (unsigned) int, (unsigned) long long int를 가지고 각각의 크기와 표현 범위를 알자.

✅ 2. 위의 저장공간에 이진수를 넣기 위해 고생하셨던 컴퓨터 공학자들의 노고를 기리며, 이진수의 표현 방법들과 각각의 특징을 알자.

✅ 3. 언더플로우와 오버플로우를 이해한다.

⚠️ 프로젝트를 하다 보면 언더플로우와 오버플로우는 생각보다 자주 발생하는 문제이다. 문제 해결에 필요한 자료형을 선택하자.

💣 과제, 위에서 해결하지 못했던 2147483648을 표현해보자. (난이도 下, 주의, formatting이 달라져야 한다. 검색해보자(%d는 int형을 위한 format이기 때문))

🔜 더 공부해보기,

  1. 1의 보수법의 특징은 이진수 간 계산이 쉽다고 했다. 이진수의 계산에 대해서 배워보자.

  2. 과제에서는 long long int의 formatting에 대해서 다루었다. short int, long int, unsigned int 등 다양한 formatting을 공부해보자.

  3. 우리 강의에서는 오버플로우를 만들어보았다. 언더플로우도 직접 만들어보고, 오버플로우와 언더플로우가 발생했을 때 어떤 숫자가 나오는지 공부해보자.