CSAPP 2장 정리 (1): 정보 저장과 정수 표현
CSAPP 2장을 읽으면서 복습할 겸 내용을 정리해 보려고 한다. 2장의 양이 많기 때문에 2.1장과 2.2장을 먼저 다룬다. 2.1장에서는 컴퓨터가 정보를 저장하고 표현하는 기본적인 방법을, 2.2장에서는 정수를 표현하는 두 가지 핵심 방식을 설명한다. 1장에서 봤듯이, 모든 정보는 비트의 연속으로 표현되며 이 비트를 해석하는 **문맥(context)**에 따라 의미가 달라진다는 것이 핵심이다.
CSAPP 2.1장: 정보 저장 (Information Storage)
1. 16진수 표기법
컴퓨터의 언어인 2진수로 표현된 데이터를 사람이 이해하기는 힘들고, 10진수로 표현하면 2진수로 다시 변환하기가 복잡하다. 따라서 이 둘의 장점을 모두 가진 16진수를 사용한다. 16진수는 4개의 비트를 한 자리로 간결하게 표현할 수 있어 2진수와 변환이 매우 쉽다.
2. 데이터 크기
컴퓨터는 **워드(word)**라는 기본 단위로 데이터를 처리하며, 이 크기는 가상 주소 공간의 한계를 결정한다. 최근에는 32비트(약 4GB)에서 64비트(약 16EB)로 전환되는 추세다. C언어의 int, long과 같은 데이터 타입의 실제 크기는 이 워드 크기와 컴파일 환경에 따라 달라질 수 있다.
- 비트 단위로 데이터를 처리하면 안 되나? 라는 의문을 가질 수 있다. 조사해보니, 그렇게 되면 주소 지정의 최소 단위가 워드인데 1비트를 위해 64비트(8바이트) 공간을 할당하는 엄청난 낭비가 발생한다. 또한 관리 측면에서도, 가상 메모리를 **페이지(page)**라는 큰 덩어리로 관리하는데 비트 단위로 관리하게 된다면, 각 비트에 주소를 매핑하는 페이지 테이블이 천문학적으로 커져 사실상 구현이 불가능해진다.
3. 주소 지정과 바이트 순서
여러 바이트로 구성된 데이터를 메모리에 저장하는 순서에 따라 리틀 엔디안과 빅 엔디안으로 나뉜다.
- 리틀 엔디안 (Little Endian): **가장 덜 중요한 바이트(LSB)**부터 저장하는 방식이다. (예: Intel, AMD 프로세서)
- 빅 엔디안 (Big Endian): **가장 중요한 바이트(MSB)**부터 저장하는 방식이다. (예: IBM, Oracle 프로세서)
- 두 방식은 특정 운영체제나 회사로 명확히 나뉘지 않으며, 최신 칩들은 두 가지 방식을 모두 지원하기도 한다. 네트워크 통신 시에는 정해진 표준에 맞춰 바이트 순서를 변환한다.
4. 문자열 및 코드 표현
- C언어에서 문자열은 아스키(ASCII) 코드로 표현된 문자들의 배열이며, NULL 문자(
\0)를 만나면 문자열의 끝으로 판단한다. - 이러한 문자열의 길이를 확인할 때는 SIMD(Single Instruction, Multiple Data) 기술이 활용된다. 이는 하나의 명령어로 여러 데이터를 병렬 처리하는 기술로, 문자열을 16바이트와 같은 큰 덩어리 단위로 읽어 한 번에 NULL 문자가 있는지 검사한다. NULL이 없다면 길이에 16을 더하고 다음 덩어리로 넘어가는 방식으로 고속 처리가 가능하다.
CSAPP 2.2장: 정수 표현 (Integer Representations)
1. 정수 데이터 타입
C언어는 정수를 부호 없는(Unsigned) 타입과 부호 있는(Signed) 타입으로 나누어 다룬다.
- 부호 없는 정수: 0 또는 양수만을 표현한다.
- 부호 있는 정수: 2의 보수 표현 방식을 사용하며, 표현 가능한 음수 범위가 양수 범위보다 1만큼 더 크다. (예: 8비트
char는 -128 ~ +127)
2. 부호 없는 정수 인코딩
부호 없는 정수는 전통적인 2진수 표기법을 따른다. w비트는 0부터 2^w - 1까지의 모든 수를 고유하게 표현할 수 있으며, 각 비트는 2^i의 양의 가중치를 가진다.
3. 2의 보수 인코딩
대부분의 컴퓨터는 부호 있는 정수를 표현하기 위해 2의 보수 방식을 사용한다. 이 방식의 가장 큰 특징은 최상위 비트(MSB)를 **부호 비트(sign bit)**로 사용하며, 이 비트가 음의 가중치를 갖는다는 점이다.
- 왜 2의 보수를 사용하는가?: 가장 큰 이유는 하드웨어 구현의 단순성 때문이다. 2의 보수 체계에서는 부호 없는 정수와 부호 있는 정수의 덧셈이 비트 수준에서 동일하게 동작하므로, 하나의 덧셈기 회로로 두 연산을 모두 처리할 수 있어 CPU 설계가 매우 효율적이게 된다.
4. 부호 있는 정수와 부호 없는 정수 간 변환
C언어에서 두 타입 간의 변환이 일어날 때, 대부분의 시스템은 비트 패턴은 그대로 유지하고 숫자 값의 해석만 바꾼다. 이 원칙은 프로그래밍에서 오류를 유발할 수 있다.
- 예를 들어
-1 < 0U비교문은거짓(false)이 된다. C언어 규칙에 따라 부호 있는 정수-1이 부호 없는 정수로 변환되면서 매우 큰 양수 값으로 해석되기 때문이다.
5. 비트 표현 확장 및 축소
데이터 타입을 변환할 때 값의 의미는 다음과 같이 보존된다.
- 영 확장 (Zero Extension): 부호 없는 수를 더 큰 타입으로 변환할 때 앞쪽에 0을 채운다.
- 부호 확장 (Sign Extension): 2의 보수 수를 더 큰 타입으로 변환할 때 부호 비트를 앞쪽에 복사하여 채운다.
- 숫자 축소 (Truncating): 큰 타입의 데이터를 작은 타입에 담으면 상위 비트들이 버려지며, 이는
mod연산과 유사하게 동작해 값의 변화를 일으킬 수 있다.