CS · Encoding · Backend

비트와 바이트 - 자리가 하나 늘면 세상이 두 배가 된다

컴퓨터가 담는 칸은 켜짐과 꺼짐 둘뿐이다. 그 칸이 하나 늘 때마다 담을 수 있는 것이 두 배가 되고, 여덟 개를 묶은 바이트가 모든 이야기의 기본 단위가 된다. 16진수가 왜 굳이 끼어드는지도 여기서 풀린다.

·0과 1로 어떻게 담나 2편
목차
  1. 칸 하나에는 켜짐 아니면 꺼짐
  2. 스위치가 하나 늘면 경우가 두 배가 된다
  3. 여덟 개를 한 묶음으로 센다
  4. 16진수는 사람이 읽으려고 있다
  5. 자리 수가 곧 한계다
  6. 여러 바이트를 늘어놓는 순서
  7. 실무에서: 비트를 직접 만질 일은 드물지만
  8. 정리

앞 글에서 컴퓨터가 든 것은 수뿐이라고 했다. 그 수가 실제로 어떤 모양으로 들어 있는지 본다.

칸 하나에는 켜짐 아니면 꺼짐

벽에 전등 스위치가 한 줄로 붙어 있다고 하자. 스위치 하나가 할 수 있는 말은 둘뿐이다. 켜짐 아니면 꺼짐.

컴퓨터의 가장 작은 칸도 똑같다. 전압이 높거나 낮고, 자성이 이쪽이거나 저쪽이다. 그걸 10으로 적기로 한 것이 비트다.

스위치 하나로는 두 가지밖에 못 말한다. 그런데 스위치를 여러 개 두고 켜짐과 꺼짐의 조합을 보면 이야기가 달라진다.

스위치가 하나 늘면 경우가 두 배가 된다

스위치를 하나 더 붙이면 표현할 수 있는 경우가 얼마나 늘까. 하나 더 느는 게 아니라 두 배가 된다.

스위치 1개 2가지
2개 4가지
3개 8가지
4개 16가지
5개 32가지
6개 64가지
스위치 8개(바이트 하나)면 256가지 - 맨 아랫줄을 네 번 이어 붙인 길이다

자리가 하나 늘 때마다 담을 수 있는 것이 갑절이 된다. 늘어나는 게 아니라 배로 뛴다.

이유는 단순하다. 새 스위치가 꺼진 경우에 원래 조합 전부가 그대로 있고, 켜진 경우에 또 한 벌이 생긴다. 그래서 매번 갑절이다.

스위치경우의 수
1개2
2개4
4개16
8개256
16개65,536
32개약 43억

여덟 개만 되어도 256가지다. 자리를 하나 늘리는 값은 늘 배로 돌아온다. 이 감각이 이 시리즈 내내 되풀이해서 나온다.

여덟 개를 한 묶음으로 센다

비트를 여덟 개 묶은 것이 바이트다. 파일 크기도, 메모리 용량도, 네트워크로 오가는 양도 전부 이 단위로 센다.

왜 하필 여덟이냐는 물리 법칙이 아니라 역사와 편의다. 초기 컴퓨터들은 6비트, 7비트, 9비트를 제각기 썼다. 그러다 영문 글자 하나를 담기에 7비트로는 빠듯하고, 2의 거듭제곱이라 절반씩 자르기 좋은 8비트가 자리를 잡았다.

중요한 건 여기서부터다. 바이트는 담는 그릇의 크기일 뿐, 그 안에 든 게 무엇인지는 말해 주지 않는다. 어떤 바이트가 글자인지 숫자인지 색인지는 앞 글에서 본 그대로 약속이 정한다.

16진수는 사람이 읽으려고 있다

바이트 하나를 있는 그대로 적으면 01001000이다. 여덟 자리를 눈으로 세다 보면 금세 틀린다.

그렇다고 십진수로 72라고 적으면 다른 문제가 생긴다. 십진수는 비트 경계와 아귀가 안 맞는다. 72만 보고는 어느 자리가 켜져 있는지 되짚을 수 없다.

0100 1000
4 8
16진수로 적으면 - 네 자리씩 접혀 글자 하나가 된다
0100 1000
72
십진수로 적으면 - 어느 자리가 켜졌는지 되짚을 수 없다

같은 바이트다. 16진수는 자르는 자리가 비트 경계와 딱 맞아 두 글자로 갈라지고, 십진수는 갈라지지 않는다.

16진수가 하는 일이 이거다. 비트 네 개가 정확히 16진수 한 글자가 된다. 그래서 바이트 하나는 언제나 16진수 두 글자이고, 자릿수가 흔들리지 않는다. 0100100048, 앞 네 자리 01004, 뒤 네 자리 10008이다. 자르는 자리가 눈에 그대로 보인다.

바이트를 다루는 도구들이 하나같이 16진수로 보여 주는 이유가 이것이다. 색을 #ff6600으로 적는 것도, 해시값a3f5c9...처럼 보이는 것도 같은 사정이다. 사람이 비트 묶음을 눈으로 확인하려고 쓰는 표기법이다.

참고

16진수는 09 다음에 af를 쓴다. a가 10, f가 15다. 프로그램에서는 0x48처럼 0x를 앞에 붙여 “이건 16진수다”라고 밝히는 경우가 많다. 표기만 다를 뿐 저장된 비트는 완전히 같다.

자리 수가 곧 한계다

스위치가 여덟 개면 256가지를 말할 수 있다. 뒤집으면 257번째는 말할 수 없다는 뜻이다.

여기가 이 시리즈에서 가장 자주 사고가 나는 자리다. 담는 칸의 자리 수는 정해져 있고, 그 칸에 안 들어가는 값을 밀어 넣으면 조용히 이상한 값이 된다. 다음 글에서 볼 정수의 넘침이 정확히 이 이야기다.

글자 쪽도 마찬가지다. 바이트 하나에 글자 하나를 담기로 하면 세상의 글자가 256개 안쪽이어야 한다. 영어는 그 안에 들어갔지만 한글과 한자는 어림도 없다. 이 시리즈 중반이 통째로 그 문제를 푸는 이야기다.

여러 바이트를 늘어놓는 순서

한 바이트에 안 들어가는 수는 여러 바이트에 나눠 담는다. 그런데 여기서 컴퓨터마다 갈리는 지점이 하나 있다. 큰 자리를 앞에 둘 것인가, 뒤에 둘 것인가.

0x12345678이라는 수를 네 바이트에 담는다고 하자.

방식저장된 순서
큰 자리부터12 34 56 78
작은 자리부터78 56 34 12

앞엣것이 사람이 읽는 순서와 같다. 뒤엣것은 뒤집혀 보이지만, 계산할 때 작은 자리부터 처리하는 회로에는 이쪽이 편하다. 지금 쓰는 대부분의 PC는 뒤엣것을 쓴다.

이게 문제가 되는 자리는 컴퓨터 밖으로 나갈 때다. 파일로 저장하거나 네트워크로 보낼 때 양쪽 순서가 다르면 수가 통째로 달라진다. 그래서 네트워크 프로토콜은 대개 순서를 큰 자리부터로 못 박아 두고, 어떤 파일 형식은 맨 앞에 “나는 이 순서로 적었다”는 표식을 넣는다. 이 시리즈 뒤에서 볼 BOM이 그 표식 중 하나다.

한 바이트 안에서는 이런 문제가 없다. 순서 이야기는 여러 바이트를 늘어놓을 때만 생긴다. 그래서 다음 글부터 볼 UTF-8은 이 걱정이 아예 없고, 두 바이트씩 묶어 쓰는 방식은 이 표식이 필요하다.

실무에서: 비트를 직접 만질 일은 드물지만

요즘 애플리케이션 코드에서 비트를 손으로 켜고 끄는 일은 거의 없다. 그런데도 이 층이 얼굴을 내미는 자리가 있다.

  • 덤프를 볼 때 - 파일이 깨졌을 때 16진수로 열어 보면 첫 몇 바이트에서 원인이 드러나는 경우가 많다. 마지막 편에서 실제로 그렇게 되짚는다.
  • 플래그를 한 칸에 몰아 담을 때 - 권한이나 옵션 여러 개를 비트 하나씩에 배정해 정수 한 개로 들고 다니는 방식이다. 리눅스 파일 권한 755가 그 꼴이다.
  • 크기를 셀 때 - 1KB가 1000바이트인지 1024바이트인지가 갈린다. 저장장치 제조사는 1000으로 세고 운영체제는 1024로 세는 바람에, 산 용량보다 적게 보이는 일이 벌어진다. 둘을 갈라 부르려고 1024 쪽을 KiB라 쓰기도 한다.

세 가지 다 “비트를 다룰 줄 알아야” 하는 일은 아니다. 바이트가 여덟 자리짜리 칸이라는 걸 알면 설명이 되는 일들이다.

정리

  • 비트는 켜짐과 꺼짐 둘뿐인 칸이고, 자리가 하나 늘면 경우의 수는 두 배가 된다.
  • 여덟 비트를 묶은 것이 바이트다. 여덟은 물리 법칙이 아니라 역사와 편의가 정했다.
  • 16진수는 비트 네 개가 정확히 한 글자라 자르는 자리가 눈에 보인다. 그래서 도구들이 16진수로 보여 준다.
  • 자리 수는 곧 한계다. 여덟 자리로는 256가지밖에 못 말하고, 그 밖의 값은 조용히 어긋난다.
  • 바이트는 그릇의 크기일 뿐이다. 그 안에 든 것이 무엇인지는 여전히 약속이 정한다.

다음 글은 이 한계를 정면으로 본다. 자리가 모자랄 때 수가 어디로 사라지는지가 주제다.