핵심 요약
- N 주소 명령어 형식은 CPU 명령어 내 오퍼랜드(operand) 필드의 개수를 의미하며, ISA 관점에서 스택/누산기/레지스터 기반 모델과 강하게 연관됩니다.
- 0주소 형식은 주로 스택, 1주소 형식은 누산기, 2~3주소 형식은 범용 레지스터를 기반으로 동작하며 각기 다른 장단점을 가집니다.
- 오퍼랜드 필드가 많을수록(예: 3주소 형식) 코드는 직관적이고 짧아질 수 있지만, 명령어 인코딩은 길어지기 쉽습니다.
- 현대 ISA는 단일 규칙만 쓰기보다, 설계 철학에 따라 여러 형식(0/1/2/3주소)이 혼재하는 경우가 많습니다.
목차
- 1. 명령어 주소 형식이란 무엇인가?
- 2. 명령어의 구조와 ISA 설계의 비밀
- 3. N 주소 명령어 형식 유형별 상세 분석 및 예제 (핵심)
- 4. 형식별 장단점 비교 및 트레이드오프 분석
- 5. 결론 (Outro)
- 자주 묻는 질문 (FAQ)
CPU가 “두 수를 더하라”는 명령을 받았을 때, 데이터가 어디에 있는지 어떻게 알 수 있을까요? 그리고 그 데이터의 위치를 몇 개나 동시에 지정해야 할까요?
우리가 무심코 사용하는 스마트폰과 PC의 CPU는 ISA 설계 단계에서 이 질문에 대한 기본 철학을 갖고 있습니다. 명령어가 오퍼랜드를 몇 개나 명시하느냐에 따라, ISA 수준에서 스택/누산기/레지스터 중심의 데이터 흐름이 달라지는 경향이 있기 때문입니다. 오늘은 컴퓨터 구조의 핵심이자 명령어 집합 구조(ISA)의 기본 뼈대가 되는 N 주소 명령어 형식 개념에 대해 깊이 있게, 하지만 아주 쉽게 알아보겠습니다.

1. 명령어 주소 형식이란 무엇인가?
N 주소 명령어 형식 개념은 CPU 명령어 집합(ISA) 설계 시, 하나의 명령어 내에 포함되는 명시적 오퍼랜드(operand) 필드의 개수를 의미합니다. (전통적인 교재 분류로 0~3주소를 많이 다루지만, ISA/명령에 따라 4개 이상이 등장하기도 합니다.)
쉽게 말해, CPU에게 일을 시킬 때 “누구랑 누구를 더해서 어디에 둬라”라고 말할지(3주소), 아니면 “일단 얘를 더해”라고 짧게 말할지(0~1주소)를 결정하는 규칙입니다. 명령어는 컴퓨터가 이해하는 언어의 문법과도 같습니다. 문법이 복잡하면 한 문장으로 많은 정보를 전달할 수 있지만 문장이 길어지고, 문법이 단순하면 문장은 짧아지지만 여러 번 말해야 하는 원리와 같습니다.
이 오퍼랜드 필드의 개수(N)는 하드웨어 복잡도, 코드 길이(명령 수), 그리고 명령어 인코딩 길이 사이의 트레이드오프에 영향을 줍니다.
에디터의 추가 노트
명령어(Instruction)는 크게 ‘무엇을 할지(Opcode)’와 ‘누구를 대상으로 할지(Operand)’로 나뉩니다. 이때 N은 명시적으로 표기되는 Operand의 개수를 뜻합니다. 다만 현대 ISA는 한 가지 형식만 고집하지 않고, 명령 종류별로 0/1/2/3주소가 섞여 있는 경우가 많습니다. 또한 JVM처럼 ISA(바이트코드)는 스택 기반(0주소 느낌)이어도, 실제 실행(JIT 후)은 레지스터 기반으로 최적화되는 등 ISA와 내부 구현은 달라질 수 있습니다.

2. 명령어의 구조와 ISA 설계의 비밀
명령어 형식이 단순한 규칙 같지만, 사실은 하드웨어 설계(ISA)와 직결되는 매우 중요한 요소입니다.
명령어의 기본 구조
컴퓨터의 명령어는 일반적으로 다음과 같은 비트(Bit) 구조를 가집니다:
- Opcode (연산자): 수행할 동작을 나타냅니다. (예: ADD, LOAD, MUL)
- Operand Field (오퍼랜드부): 연산에 필요한 데이터(또는 그 위치)를 지정합니다. (레지스터 번호, 즉시값, 메모리 주소/주소지정 정보 등)
여기서 ‘ISA에서의 N 주소 형식’의 N값이 커질수록(오퍼랜드를 많이 명시할수록) 명령어 인코딩이 길어지기 쉽습니다. 반대로 N이 작으면 명령어는 단순해지지만, 같은 작업을 수행하기 위해 더 많은 수의 명령어가 필요해질 수 있습니다.
ISA(명령어 집합 구조)와의 관계
이 주소 방식은 ‘컴퓨터 명령어 형식 종류’를 이해하는 기초가 됩니다. 인텔의 x86 아키텍처나 ARM, MIPS 같은 다양한 ISA는 각자의 설계 철학에 맞춰 여러 형식을 혼합합니다.
- CISC(복합 명령어): x86 계열은 전통적으로 2오퍼랜드 형식이 많아 코드 밀도 측면에서 강점이 있습니다(단, SIMD/확장 명령에선 3오퍼랜드도 흔합니다).
- RISC(축소 명령어): ARM이나 MIPS는 레지스터-레지스터 연산(3오퍼랜드 스타일)이 많고, 메모리 접근은 LOAD/STORE로 분리하는 경향이 큽니다.
이러한 명령어 형식은 교육적으로 3주소 형식, 2주소 형식, 1주소 형식, 0주소 형식의 네 가지로 자주 분류됩니다. 이제 각 형식이 실제로 어떻게 작동하는지 구체적인 예제와 함께 살펴보겠습니다.
에디터의 추가 노트
ISA 설계자는 ‘코드 밀도(메모리/캐시 효율)’와 ‘디코딩/파이프라인 단순성’, ‘레지스터 파일 구조’ 사이에서 끊임없이 줄타기를 합니다. 예를 들어, 3오퍼랜드 스타일은 컴파일러 최적화에 유리한 반면, 오퍼랜드 필드가 늘어 인코딩이 길어지기 쉽습니다. 반대로 0오퍼랜드(ALU 명령) 스타일은 디코딩이 단순하지만, 스택에 값을 넣고 빼는 명령이 늘어날 수 있습니다.

3. N 주소 명령어 형식 유형별 상세 분석 및 예제 (핵심)
가장 중요한 파트입니다. 4가지 형식을 비교하기 위해 동일한 수식 X = (A + B) × (C + D)를 계산하는 코드를 작성해 보겠습니다. 각 방식에 따라 N 주소 명령어 형식 예제 코드가 어떻게 달라지는지 눈여겨보세요.
Type 1: 3주소 형식 (일반 레지스터 머신)
주로 RISC 계열(MIPS, 최신 ARM 등)에서 흔히 볼 수 있는 스타일입니다.
- 형식: Opcode Dest, Src1, Src2 (예: ADD R1, R2, R3)
- 특징: 피연산자 2개(Src1, Src2)와 결과 저장소(Dest)를 모두 명시합니다. 한 줄에 수식을 표현하기 쉬워 코드가 간결해질 수 있지만, 오퍼랜드 필드가 늘어 인코딩이 길어지기 쉽습니다.
[예제 코드] (개념 설명을 위한 의사코드)
ADD R1, A, B ; R1에 (A+B) 저장
ADD R2, C, D ; R2에 (C+D) 저장
MUL X, R1, R2 ; R1*R2를 X에 저장
분석: 개념적으로는 매우 직관적입니다. 참고: 전형적인 RISC(로드/스토어)에서는 A,B,C,D는 먼저 LOAD로 레지스터에 올린 뒤 레지스터끼리 ADD/MUL 하고, 마지막에 STORE로 X에 저장하는 형태가 됩니다.
Type 2: 2주소 형식 (전통적 2오퍼랜드 스타일)
인텔 x86 등에서 전통적으로 많이 보이는 형식입니다.
- 형식: Opcode Dest, Src (예: ADD R1, R2)
- 특징: 목적지(Dest)가 입력이자 동시에 출력이 됩니다. 즉, Dest <- Dest op Src 형태로 동작합니다. 이 때문에 원본 데이터 보존을 위해 MOVE가 자주 필요할 수 있습니다.
[예제 코드]
MOV R1, A
ADD R1, B
MOV R2, C
ADD R2, D
MUL R1, R2
MOV X, R1
분석: 3오퍼랜드 스타일보다 보조 이동(MOV)이 늘어 코드가 길어질 수 있습니다. 대신 2오퍼랜드는 명령어 인코딩을 단순/압축하기 쉬워(특히 가변 길이 ISA에서) 코드 밀도 측면에서 장점이 있을 수 있습니다.
Type 3: 1주소 형식 (누산기 구조)
초기 컴퓨터나 단순한 구조에서 자주 설명되는 방식입니다.
- 형식: Opcode Operand (예: LOAD A)
- 특징: 두 번째 피연산자는 암묵적으로 누산기(Accumulator, AC)를 사용한다고 약속되어 있습니다.
[예제 코드]
LOAD A
ADD B
STORE T
LOAD C
ADD D
MUL T
STORE X
분석: 임시 저장소(T)가 필요해지면서 코드가 길어지고, 프로그래머/컴파일러가 데이터 흐름을 더 신경 써야 합니다.
Type 4: 0주소 형식 (스택 머신)
자바 가상 머신(JVM) 바이트코드처럼, 스택 기반 모델에서 자주 등장하는 방식입니다.
- 형식: (ALU 연산은) Opcode만 사용 (예: ADD, MUL)
- 특징: ALU 연산은 스택 최상단(TOS) 값 2개를 꺼내 연산하고 결과를 다시 push합니다. 단,
PUSH/POP처럼 값을 적재/저장하는 명령은 보통 operand(대상)를 가집니다.
[예제 코드]
PUSH A
PUSH B
ADD
PUSH C
PUSH D
ADD
MUL
POP X
분석: ALU 연산(ADD/MUL)은 주소 필드가 없어 단순하지만, 값을 쌓고 꺼내는 명령이 늘어 전체 명령 수가 증가할 수 있습니다.
에디터의 추가 노트
0오퍼랜드(ALU) 스타일은 오퍼랜드 해석 부담이 작지만, 스택 적재/저장 명령이 늘어날 수 있습니다. JVM은 이런 “스택 기반 ISA”를 사용하되, 실제 실행(JIT 후)에서는 레지스터 기반으로 강하게 최적화되기도 합니다.

4. 형식별 장단점 비교 및 트레이드오프 분석
단순히 방식이 다른 것이 아니라, 각각의 방식은 하드웨어 자원과 성능 사이의 트레이드오프(Trade-off)를 반영하고 있습니다. N 주소 명령어 형식 장단점을 한눈에 비교해 보겠습니다.
명령어 형식 비교 요약표
| 구분 | 3주소 형식 | 2주소 형식 | 1주소 형식 | 0주소 형식 |
|---|---|---|---|---|
| 기반 구조 | GPR (범용 레지스터) | GPR (범용 레지스터) | Accumulator (누산기) | Stack (스택) |
| 명령어 길이 | 대체로 김 (오퍼랜드 필드 많음) | 중간 | 짧음 | ALU 명령은 가장 짧음 |
| 코드 길이(명령 수) | 대체로 짧아질 수 있음 | 중간 | 김 | 대체로 김 |
| 하드웨어/디코딩 단순성 | 상대적으로 복잡해질 수 있음 | 중간 | 단순 | ALU는 단순(스택 관리 필요) |
| 주요 사용처 | RISC (ARM, MIPS, RISC-V 등) | x86 등 (형식 혼재) | 교육용/초기 구조 예시 | JVM/스택 VM |
상세 트레이드오프 분석
- 3주소 형식의 딜레마: 코드가 직관적이고 명령 수를 줄이기 쉬우나, 오퍼랜드 필드가 늘어 인코딩이 길어질 수 있습니다(코드 밀도/캐시 효율과 연관).
- 2주소 형식의 균형: 명령어 길이와 코드 길이 사이에서 현실적인 절충을 노립니다. 다만 목적지(dest)가 덮어써지므로 컴파일러/프로그래머가 데이터 보존을 위해 MOV 등을 추가할 수 있습니다.
- 0주소(ALU) 형식의 특징: ALU 연산은 간단하지만, 스택 적재/저장 명령이 늘어 전체 명령 수가 증가할 수 있습니다.
에디터의 추가 노트
x86 프로세서도 내부적으로는 복잡한 명령을 작은 마이크로 오퍼레이션(uOp)으로 쪼개어 처리하는 등, ISA와 내부 구현은 다를 수 있습니다. 결국 중요한 것은 특정 응용 분야(서버, 모바일, 임베디드)에 맞는 효율성을 찾는 것입니다.
5. 결론 (Outro)
지금까지 N 주소 명령어 형식 개념부터 0~3주소 형식의 차이점까지 상세히 살펴보았습니다.
결국 명령어 주소 형식의 선택은 “오퍼랜드를 더 많이 명시해 코드를 간결하게 만들 것인가” vs “오퍼랜드를 덜 명시해 명령어 형식을 단순/압축하기 쉽게 만들 것인가” 사이의 줄다리기입니다.
오늘 배운 내용을 확실히 내 것으로 만들기 위해, 본문에 나온 X = (A + B) × (C + D) 예제를 보지 않고 종이에 직접 각 방식대로 코드를 작성해 보세요. 레지스터와 스택의 데이터 흐름이 머릿속에 생생하게 그려질 것입니다.
이 개념이 이해되셨다면, 컴퓨터 구조의 다음 단계인 ‘RISC vs CISC’ 또는 CPU 성능을 극대화하는 ‘파이프라이닝(Pipelining)’ 기술에 대해 찾아보시는 것을 추천합니다. 여러분의 컴퓨터 구조 완전 정복을 응원합니다!
자주 묻는 질문 (FAQ)
Q: 3주소 형식이 코드가 가장 짧으니 무조건 좋은 것인가요?
A: 반드시 그렇지는 않습니다. 명령 수는 줄어들 수 있지만, 오퍼랜드 필드가 늘어 인코딩이 길어져 코드 밀도/캐시 효율 측면에서 불리해질 수 있습니다. 또한 ISA와 내부 구현의 선택은 목적(전력/성능/면적)에 따라 달라집니다.
Q: 2주소 형식에서 원본 데이터가 사라지는 문제는 어떻게 해결하나요?
A: 연산 전에 MOV로 다른 레지스터에 복사해 두는 방식이 대표적입니다. 그래서 2주소 스타일은 보조 이동 명령이 늘어날 수 있습니다.
Q: 현대의 PC나 스마트폰은 어떤 방식을 주로 사용하나요?
A: 전통적으로 PC의 x86은 2오퍼랜드 형식이 많지만, 0/1/3 오퍼랜드 형식도 혼재합니다(특히 SIMD). ARM 계열은 레지스터-레지스터 연산(3오퍼랜드 스타일)이 많고 메모리 접근은 LOAD/STORE로 분리하는 경향이 큽니다. 최근에는 내부 구현에서 기술을 융합해 효율성을 극대화하기도 합니다.