우리는 코딩을 할 때 너무나도 당연하게 a = 10
과 같은 코드를 작성합니다. 개발자에게 'a'는 10이라는 값을 담는 그릇처럼 직관적으로 이해됩니다. 하지만 컴퓨터에게 'a'라는 이름은 아무 의미가 없습니다.
컴퓨터는 오직 0과 1, 그리고 메모리 주소로 세상을 이해합니다.
그렇다면 우리가 작성한 이 간단한 한 줄의 코드는 어떻게 컴퓨터가 이해하고 실행할 수 있는 기계어 코드로 변환될까요?
변수 'a'와 'b'를 더하는 간단한 과정을 통해, 그 깊고 흥미로운 내부 동작 원리를 단계별로 파헤쳐 봅시다.
1단계: 개발자와의 약속, '변수 선언'
a = 10
b = 20
프로그래밍에서 a = 10
과 같은 변수 선언은 특정 데이터를 저장할 공간에 사람이 이해하기 쉬운 이름을 붙여주는 과정입니다.
'a'라는 이름은 컴퓨터를 위한 것이 아니라, 코드를 작성하고 읽는 개발자를 위한 기호(Symbol)이자 약속입니다. 컴퓨터는 이 심볼을 보고 "아, 개발자가 무언가를 저장하고 싶어 하는구나"라고 인지하고 다음 단계를 준비합니다.
2단계: 데이터의 집, '메모리 주소'에 저장
컴퓨터는 변수 'a'와 'b'의 데이터를 실제 물리적인 저장 공간인 메모리(RAM)에 저장합니다. 이때 각각의 데이터는 고유한 주소값을 할당받게 됩니다.
a
의 값10
은 메모리의 특정 주소 (예:0x1000
)에 저장됩니다.b
의 값20
은 다른 메모리 주소 (예:0x2000
)에 저장됩니다.
이처럼 모든 변수는 메모리 내의 고유한 주소를 통해 접근되며, 'a'나 'b'라는 이름은 이 주소를 가리키는 별명인 셈입니다.
3단계: CPU의 작업 공간, '레지스터'로 이동
CPU는 연산을 수행할 때 매번 느린 메모리에 접근하는 대신, CPU 내부에 있는 초고속 임시 저장 공간인 레지스터(Register)를 사용합니다. a + b
연산을 수행하기 위해 컴퓨터는 다음과 같이 동작합니다.
- 메모리 주소
0x1000
에 저장된 값10
을 CPU의 레지스터(예:eax
)로 가져옵니다. - 메모리 주소
0x2000
에 저장된 값20
을 다른 레지스터(예:ebx
)로 가져옵니다.
이제 CPU는 가장 빠른 작업 공간에 연산에 필요한 모든 재료를 준비해두었습니다.
4단계: 컴퓨터의 진짜 언어, '인스트럭션' 생성
개발자가 작성한 a + b
라는 코드는 컴파일 과정을 거쳐 CPU가 직접 실행할 수 있는 기계어 명령어, 즉 인스트럭션(Instruction)으로 변환됩니다. 이 과정을 좀 더 이해하기 쉬운 어셈블리 코드로 표현하면 다음과 같습니다.
mov eax, [0x1000] ; 메모리 주소 0x1000의 값을 eax 레지스터로 이동(move)
mov ebx, [0x2000] ; 메모리 주소 0x2000의 값을 ebx 레지스터로 이동(move)
add eax, ebx ; eax 레지스터의 값과 ebx 레지스터의 값을 더해서 결과를 eax에 저장
이제 a
와 b
라는 이름은 사라지고, 오직 메모리 주소와 레지스터, 그리고 mov
(이동), add
(덧셈)와 같은 구체적인 행동 지침만 남았습니다.
5단계 ~ 7단계: 이름과 주소를 연결하고 실행 파일로!
그렇다면 'a'라는 이름이 어떻게 0x1000
이라는 주소로 바뀌었을까요? 바로 어셈블러와 링커 덕분입니다.
- 심볼 테이블 생성 (어셈블러): 어셈블러는
a
는0x1000
,b
는0x2000
처럼, 코드에 사용된 모든 심볼(변수명, 함수명 등)과 메모리 주소를 연결하는 '심볼 테이블'이라는 목록을 만듭니다. - 주소 결정 (링커): 링커는 여러 코드 조각(오브젝트 파일)을 하나로 합치고, 심볼 테이블을 참조하여
mov eax, [a]
와 같이 아직 불완전한 주소들을 최종적인 실제 메모리 주소(mov eax, [0x1000]
)로 확정합니다. - 최종 실행 파일 생성: 이 모든 과정을 거치면 마침내 최종 실행 파일이 만들어집니다. 이 파일 안에는 더 이상 'a'나 'b'와 같은 변수 이름이 존재하지 않습니다. 오직 CPU가 따라야 할 인스트럭션과 데이터가 저장될 메모리 주소 정보만 담겨있을 뿐입니다.
운영체제가 이 파일을 실행하면, CPU는 파일에 적힌 인스트럭션을 순서대로 읽어 메모리 주소에 접근하고, 레지스터를 활용해 연산을 수행하며 우리가 의도했던 a + b
의 결과를 만들어내는 것입니다.
결론: 변수명은 개발자를 위한 배려
결국 a = 10
이라는 코드는 인간의 편의를 위해 고안된 고도의 추상화입니다. 우리가 사용하는 변수명은 컴파일과 링킹 과정에서 자신의 역할을 다하고 사라지는 안내자인 셈입니다.
그 이면에서는 변수가 메모리 주소로, 코드가 CPU 인스트럭션으로 변환되는 복잡하지만 정교한 과정이 숨어있습니다. 오늘 당장 사용하는 변수 하나에도 컴퓨터 과학의 깊은 원리가 담겨있다는 사실, 재밌죠?