한양과학기술고등학교

에듀테크 프로젝트 대시보드

주소 지정 방식 (Addressing Modes)

주소 지정 방식 (Addressing Modes)

명령어는 "무엇을 계산할지"뿐 아니라 "그 피연산자가 어디 있는지"도 지정해야 한다 — 그 방법이 다섯 가지다

문제 상황. ADD R1, X 라는 명령어가 있다고 하자. R1에 무언가를 더해야 하는데, 도대체 X는 무엇을 뜻하는가? X 자체가 더할 값인가? 아니면 값이 저장된 메모리 주소인가? 그것도 아니면 다른 레지스터 안의 값인가? 명령어의 비트 몇 개가 "X를 어떻게 해석할지"를 함께 지정하는데, 이 해석 규칙을 주소 지정 방식(Addressing Mode)이라 부른다. 같은 명령어 폭 안에서 유연성과 속도를 맞바꾸는 설계 선택이다.

1. 다섯 가지 주소 지정 방식

피연산자를 찾아가는 "경로"의 길이가 방식마다 다르다. 경로가 짧을수록(메모리를 덜 뒤질수록) 빠르지만, 대신 프로그램이 표현할 수 있는 것도 단순해진다.

즉시 주소지정 (Immediate)

예: MOV R1, #5 — 값 5 자체가 명령어 안에 박혀 있음
명령어#5
R1 = 5
메모리 접근 0회 · 가장 빠름

해석할 것도 없이 명령어 그 자체가 데이터다. 그래서 상수를 레지스터에 넣을 때 흔히 쓴다.

직접 주소지정 (Direct)

예: MOV R1, [100] — 명령어 안의 100은 메모리 주소
명령어주소 100
메모리[100]= 42
R1 = 42
메모리 접근 1회

주소로 딱 한 번 가면 값이 있다. 전역 변수처럼 위치가 고정된 데이터에 적합하다.

간접 주소지정 (Indirect)

예: MOV R1, [[200]] — 200번지에는 값이 아니라 또 다른 주소가 있음
명령어주소 200
메모리[200]= 주소 300
메모리[300]= 17
R1 = 17
메모리 접근 2회 · 느림

"포인터의 포인터" 개념. 한 번 더 거쳐야 진짜 값에 닿는다 — 그만큼 메모리 접근도 두 배다.

레지스터 주소지정 (Register)

예: ADD R1, R2 — 값이 레지스터 R2 안에 바로 있음
명령어R2
R2= 9
R1 = 9
메모리 접근 0회 · 가장 빠름

메모리 근처에도 안 간다. 레지스터는 CPU 내부에 있어 접근이 사실상 즉시다.

레지스터 간접 주소지정 (Register Indirect)

예: MOV R1, [R2] — R2 안에 메모리 주소가 들어있음
명령어R2
R2= 주소 400
메모리[400]= 55
R1 = 55
메모리 접근 1회

C의 포인터 변수와 거의 같은 개념. 레지스터가 "어디를 가리킬지"는 실행 중에 계속 바뀔 수 있다.

2. 핵심 인터랙티브 — R1은 최종적으로 어떤 값이 될까?

아래에 작은 CPU 상태(레지스터 R1·R2 + 8칸짜리 메모리)가 있다. 주소 지정 방식을 골라 한 단계씩 실행해 보면, 같은 "R1에 값 적재"라도 몇 번 메모리를 거치는지가 방식마다 다르다는 걸 체감할 수 있다. 비교를 쉽게 하려고 다섯 방식 모두 R1 ← 피연산자 형태로 통일했다 (ADD 같은 연산 명령어도 피연산자를 찾는 과정 자체는 동일하다).

R1 (목적지)
?
R2
3

경로: (아직 실행 전)

주소 지정 방식을 고르고 “다음 단계”를 눌러 시작하세요.
진행 단계
0
메모리 접근 횟수
0
R1 최종값
–

3. 비교표

방식메모리 접근 횟수속도유연성대표 활용
즉시 (Immediate)0회가장 빠름낮음 — 값이 명령어에 고정상수 초기화 (i = 0)
레지스터 (Register)0회가장 빠름중간 — 레지스터 값은 바뀌지만 개수가 적음레지스터 간 연산 (R1 += R2)
직접 (Direct)1회보통낮음 — 주소가 명령어에 고정전역 변수 접근
레지스터 간접 (Register Indirect)1회보통높음 — 실행 중 레지스터 값만 바꾸면 대상 변경포인터 변수(*p), 배열 순회, 배열 인덱싱(오프셋과 결합)
간접 (Indirect)2회가장 느림가장 높음 — 가리키는 주소 자체를 자료구조로 관리 가능포인터 배열, 함수 포인터 테이블, 이중 포인터(**pp)
속도-유연성 트레이드오프. 즉시·레지스터 방식은 빠르지만 "무엇을 가리킬지"가 컴파일 시점에 고정된다. 반대로 간접 방식으로 갈수록 실행 중에 대상을 자유롭게 바꿀 수 있는 대신 메모리를 더 많이 거쳐야 한다. 이 사이트의 명령어 집합 구조 페이지에서 다룬 "명령어 폭은 제한돼 있다"는 제약과 정확히 맞물리는 설계 선택이다.

4. C 포인터와의 대응

고급 언어의 포인터는 사실 주소 지정 방식을 소스 코드 수준에서 그대로 노출한 것이다.

// int 변수 하나와 그걸 가리키는 포인터, 포인터의 포인터
int x = 42;         // x 자체에 값 42가 저장됨
int *p = &x;        // p 안에는 "x의 주소"가 들어있다
int **pp = &p;      // pp 안에는 "p의 주소"가 들어있다

printf("%d", x);     // x를 직접 읽음        → 레지스터/직접 주소지정과 대응
printf("%d", *p);    // p가 가리키는 곳을 읽음 → 레지스터 간접 주소지정과 대응: MOV R1, [R2]
printf("%d", **pp);  // pp → p → x, 두 번 따라감 → 간접 주소지정과 대응: MOV R1, [[addr]]

*p 한 번 역참조는 메모리 접근 1회, **pp 두 번 역참조는 메모리 접근 2회 — 포인터를 많이 중첩할수록 프로그램이 실제로 느려지는 이유가 바로 이것이다.

5. 관련 페이지

한 줄 정리. 주소 지정 방식은 명령어가 "피연산자를 어디서 찾을지"를 정하는 규칙이며, 값 자체(즉시) → 레지스터 → 메모리 한 번(직접/레지스터간접) → 메모리 두 번(간접) 순으로 경로가 길어질수록 느려지지만 유연해진다 — 그리고 이 스펙트럼은 C의 변수 → *p → **pp와 정확히 같은 모양이다.