컴퓨터 구조

[2/17 비전공자가 공부하는 컴퓨터 구조 공부 기록 남기기] 명령어 구조 야근좀 제발 그만!!!!!!!

여러가지 공부를 하고 있습니다. 2025. 2. 17. 22:10

후아.... 야근 야근!!!!!! 요즘 회사가 왜이리 바쁜지 모르겠다..

 

비록 지금은 개발과 무관한 일을 하고있지만 언젠가는 개발을 하는 일의 기회가 생겼으면 좋겠다 그때를 위해 계속 난 공부한다!!! 아자

1. 컴퓨터는 우리가 작성한 코드를 바로 이해할 수 있을까?

 
#include <stdio.h>

int main() {
    printf("Hello, World");
    return 0;
}

 

C 코드(고급 언어)를 작성하면, 컴퓨터는 이를 바로 이해하고 실행할 수 있을까? 🤔
아니다! 컴퓨터는 사람이 읽기 쉬운 고급 언어를 이해하지 못한다.
컴퓨터가 이해할 수 있는 저급 언어(기계어)로 변환하는 과정이 필요하다.


 

2. 고급 언어와 저급 언어

📌 고급 언어(High-Level Language)

  • 개발자가 쉽게 이해하고 작성할 수 있는 언어.
  • 예: C, Python, Java, JavaScript 등.
  • 장점: 코드가 직관적이며 생산성이 높음.
  • 단점: 컴퓨터가 직접 실행할 수 없고 변환 과정(컴파일, 인터프리트)이 필요함.

📌 저급 언어(Low-Level Language)

  • 컴퓨터가 직접 실행할 수 있는 언어.
  • 기계어(Machine Language)와 어셈블리어(Assembly Language)로 나뉨.

🖥 기계어(Machine Language)

  • 0과 1로 이루어진 이진 코드 (Binary Code).
  • 컴퓨터가 직접 실행할 수 있는 형태.
0101 0101  →  push rbp
0101 1101  →  pop rbp
1100 0011  →  ret

🖥 어셈블리어(Assembly Language)

  • 기계어를 사람이 조금 더 읽기 쉽게 변환한 형태.
  • 특정 CPU에 맞는 명령어 집합(Instruction Set Architecture, ISA)을 사용.
  • 예:
mov eax, 1   ; eax 레지스터에 1 저장
add eax, ebx ; eax = eax + ebx
ret          ; 함수 반환
  • 장점: 기계어보다 사람이 읽기 쉬움.
  • 단점: 고급 언어보다 여전히 어렵고 하드웨어 종속적.

3. 컴퓨터가 코드를 실행하는 과정: 컴파일 vs 인터프리터

코드를 저급 언어(기계어)로 변환하는 방법은 컴파일 방식과 인터프리트 방식이 있다.

📌 컴파일 방식 (Compiled Language)

  • 코드를 한 번에 기계어로 변환하여 실행 파일(목적 코드, Object Code)을 생성.
  • 대표 언어: C, C++, Java, Rust, Go 등.
  • 장점: 실행 속도가 빠르고 최적화가 가능함.
  • 단점: 실행 전에 컴파일 과정이 필요하여 즉각적인 실행이 어려움.

🔹 컴파일 과정

  1. 소스 코드 작성 (.c 파일)
  2. 컴파일러가 저급 언어(기계어)로 변환 (.exe 또는 .out 실행 파일 생성)
  3. 실행 (CPU가 기계어를 직접 실행)

📌 인터프리터 방식 (Interpreted Language)

  • 코드를 한 줄씩 번역하며 실행.
  • 대표 언어: Python, JavaScript, Ruby, PHP 등.
  • 장점: 코드 수정 후 바로 실행 가능, 디버깅이 쉬움.
  • 단점: 실행 속도가 느림.

🔹 인터프리트 과정

  1. 소스 코드 작성 (.py, .js 파일)
  2. 인터프리터가 코드를 한 줄씩 변환하고 즉시 실행
  3. CPU가 변환된 명령어를 실행

4. 명령어 구조와 실행 방식

컴퓨터의 명령어는 "무엇을 대상으로, 무엇을 수행하라"는 구조로 이루어져 있다.

📌 명령어의 구성

  • 연산 코드 (Opcode): 수행할 연산 지정 (예: 더하기, 빼기, 메모리 로드 등).
  • 오퍼랜드 (Operand): 연산에 사용될 데이터 or 데이터의 주소.

 

더해라(수행할 연산) + 100과(연산에 사용될 데이터 혹은 연산에 사용될 데이터가 저장된 위치) + 120을 

빼라 + 메모리 32번지 안의 값과 + 메모리 33번지 안의 값을 

 

명령어는 연산 코드(수행할 연산)와 오퍼랜드(연산에 사용될 데이터 혹은 연산에 사용될 데어터가 저장된 위치)로 구성된다.


5. 주소 지정 방식 (Addressing Modes)

어쨌든 컴퓨터는 어디에 명시되어 있건간에 연산에 사용될 데이터들을 쏙쏙 찾아서 실행시킬 수 있어야 할 것임.

그 방법이 명령어 주소지정 방식이다. 

 

CPU가 연산에 사용할 데이터를 어디서 가져올 것인지를 결정하는 방법이다.
즉, 연산에 사용할 데이터(오퍼랜드)의 위치를 찾는 방식.

 

📌 주요 주소 지정 방식

주소 지정 방식설명예시

즉시 주소 지정 (Immediate) 데이터 값을 명령어 내에 직접 포함 mov eax, 10
직접 주소 지정 (Direct) 메모리 주소를 직접 명시 mov eax, [0x1000]
간접 주소 지정 (Indirect) 메모리 주소가 저장된 주소를 참조 mov eax, [ebx]
레지스터 주소 지정 (Register) 데이터를 레지스터에서 가져옴 mov eax, ebx

📌 즉시 주소 지정 (Immediate Addressing)

  • 연산에 사용할 데이터가 명령어 안에 직접 포함됨.
  • 빠르지만, 큰 데이터를 처리하기 어려움.
  • 예:
    assembly
    복사편집
    mov eax, 10 ; eax 레지스터에 10 저장

📌 직접 주소 지정 (Direct Addressing)

  • 연산할 데이터가 있는 메모리 주소를 직접 지정.
  • 예:
    assembly
    복사편집
    mov eax, [0x1000] ; 메모리 주소 0x1000의 데이터를 eax에 저장

📌 간접 주소 지정 (Indirect Addressing)

  • 오퍼랜드 필드에 유효 주소의 "주소"를 명시.
  • 메모리를 여러 번 접근해야 해서 속도가 느림.
  • 예:
    assembly
    복사편집
    mov eax, [ebx] ; ebx 레지스터에 저장된 주소에서 값을 가져옴

📌 레지스터 주소 지정 (Register Addressing)

  • 데이터를 메모리가 아닌 레지스터에서 직접 가져옴.
  • 메모리 접근이 필요 없으므로 속도가 가장 빠름.
  • 예:
    assembly
    복사편집
    mov eax, ebx ; ebx 레지스터 값을 eax로 복사

6. 결론 및 요약

컴퓨터는 우리가 작성한 고급 언어를 바로 이해하지 못한다.
컴파일러(Compiled Language)와 인터프리터(Interpreted Language) 방식으로 변환하여 실행한다.
컴퓨터 명령어는 연산 코드(Opcode) + 오퍼랜드(Operand)로 구성된다.
주소 지정 방식은 CPU가 연산할 데이터를 어디서 찾을지를 결정하는 방식이다.
레지스터를 사용하면 속도가 빠르고, 메모리를 여러 번 참조하면 속도가 느려진다.

 

 

 

더보기

우리가 작성한 소스 코드

 

#include <stdio.h>

 

int main(){

printf("Hello, World");

return o;

}

 

라는 코드를 작성했을 때 컴퓨터는 이러한 코드를 바로 직독직해해서 이해할 수 있을까?

 

아니다 이러한 언어들은 고급언어임 이건 우리 사람들이 (개발자)가 이해하고 읽기 쉽게 만들어진 언어임 (c, java, python) 등등 

 

고급 언어 -> 변환 -> 저급 언어 (명령어)

 

고급 언어 

- 개발자가 이해하기 쉽게 만든 언어

 

저급 언어

- 컴퓨터가 이해하고 실행하는 언어 

 

저급언어에는 기계어와 어셈블리어로 나뉨 

 

기계어는 이진수(0과 1)로 표현된 기계어임 16진수로도 표현하기도함 

 

기계어                       어셈블리어

0101 0101 ------> push rbp

 

0101 1101 ------> pop rbp

 

1100 0011 ------> ret 

 

어셈블리어는 0과 1로 이루어진 기계어를 읽기 편한 형태로 번역한 저급언어임 

 

우리가 사용하는 언어는 고급언어임

 

그런데 컴퓨터가 알아들을 수 있는 저급언어로 변환하는 과정을 거쳐야하는데

 

컴파일 방식과 인터프리터 방식이 존재한다.

 

먼저 컴파일 언어 

 

우리가 작성한 소스코드를 컴파일러 저급언어로 변환해주는 장치에 의해 저급언어로 변환된다.

 

정리하면 컴파일 언어로 작성된 소스 코드는 컴파일러에의해 저급 언어로 변환되고 컴파일 결과로 저급 언어인 목적 코드가 생성된다. 

 

인터프리트 언어는?

 

인터프리터에 의해 한 줄씩 실행함

소스 코드 전체가 저급 언어로 변환되기까지 기다릴 필요가 없음 

 

컴파일 언어 vs 인터프리트 언어

 

컴파일 언어는 예를 들어 영어를 모르는 친구(컴퓨터)에게 영어를 잘아는 친구가(컴파일러) 통번역본을 주는것

 

인터프리트언어는 한줄씩 한줄씩 친구에게 설명을 해주는 것 

 

하지만 이러한 개념이 완전히 양분되는 개념은 아니다.

 

명령어의 구조 

 

무엇을 대상으로, 무엇을 수행하라라는 구조를 가지고 있음 

 

더해라(수행할 연산) + 100과(연산에 사용될 데이터 혹은 연산에 사용될 데이터가 저장된 위치) + 120을 

빼라 + 메모리 32번지 안의 값과 + 메모리 33번지 안의 값을 

 

명령어는 연산 코드(수행할 연산)와 오퍼랜드(연산에 사용될 데이터 혹은 연산에 사용될 데어터가 저장된 위치)로 구성된다.

 

 

오퍼랜드가 없을 수도 여러 개 있을  수도 있다

 

오퍼랜드는 연산에 사용될 데이터가 저장된 위치가 더 많이 쓰이므로 주소 필드라고도 쓰임 

 

왜 굳이 위치를 쓸까? 

 

명령어에서 표현할 수 있는 데이터의 크기가 제한 적이기 때문임. 

 

 

연산 코드는 4비트로 고정되어 있고 나머지 오퍼랜드가 사용할 수 있는 비트 수는 고정되어 있기 떄문에 오퍼랜드가 많아지면 그만큼 사용할 수 있는 비트의 값이 적어진다.

 

하지만 명령어 주소 지정방식을 사용하면 오퍼랜드 필드로 표현할 수 있는 데이터 크기는 2의 16승개로 늘어남 

 

 

 

유효 주소 (effective address)

연산에 사용될 주소

 

결론으로 오퍼랜드 필드에는 레지스터가 담길 수도 있고 메모리 주소가 담길 수도 있고 연산 코드에 사용될 데이터가 직접적으로 명시될 수도 있음.

 

어쨌든 컴퓨터는 어디에 명시되어 있건간에 연산에 사용될 데이터들을 쏙속 찾아서 실행시킬 수 있어야 할 것임 그 방법이 명령어 주소지정 방시기다. 

 

명령어 주소 지정 방식(addressing modes)

 

연산에 사용할 데이터가 저장된 위치를 찾는 방법

유효 주소를 찾는 방법

다양한 명령어 주소 지정 방식들 

 

여기서 명령어 주소 방식으로는 

 

즉시 주소 지정 방식 (immediate addressing mode)

 

- 연산에 사용할 데이터를 오퍼랜드 필드에 직접 명시하는 것

- 가장 간단한 형태의 주소 지정 방식

- 연산에 사용할 데이터의 크기가 작아질 수 있지만 빠르다. 

 

직접 주소 지정 방식 (direct addressing mode)

 

- 오퍼랜드 필드에 유효 주소 직접적으로 명시

- 유효 주소를 표현할 수 있는 크기가 연산 코드만큼 줄어든다.

 

유효 주소를 찾아갔더니 데이터가 있더라라고 생각 

 

간접 주소 지정 방식 (indirect addressing mode)

 

- 오퍼랜드 필드에 유효 주소의 주소를 명시

- 앞선 주소 지정 방식들에 비해 속도가 느림 

 

-> 이것의 단점은 여러번 메모리를 뒤적거려야함 cpu가 메모리를 뒤적거리게 되면 속도가 줄어듦

속도 차원에서는 cpu가 메모리에 접근하는 것이 무조건적으로 적으면 좋음 

 

레지스터 주소 지정 방식 (register addressing mode)

 

- 연산에 사용할 데이터가 저장된 레지스터 명시

- 메모리에 접근하는 속도보다 레지스터에 접근하는 것이 빠름  -> 컴퓨터 구조에서 기본적으로 알고 있어야 하는 상식 

 

메모리는 cpu 밖에 있고 레지스터는 cpu 안에 있으니 당연한 것임