
학습목표
혼공컴운 1 ~ 3장
1장
컴퓨터의 핵심요소
- 컴퓨터가 이해하는 정보는 데이터와 명령어. 명령어를 통해 데이터를 다룬다
- 4가지 핵심요소와 그를 연결해주는 것: CPU, 주기억장치(메모리 - RAM과 ROM이 있지만 보통 RAM을 지칭), 보조기억장치, 입출력장치. 그리고 이를 연결해주는 시스템 버스
- CPU는 ALU(계산기), 레지스터(임시저장장치), 제어장치(부품 작동 및 명령어 해석)로 구성
돌아가는 원리
어떤 명령어를 읽기 위해 신호를 준다고 가정해보자.
[CPU 동작과정]
1. 제어장치가 메모리에 '메모리 읽기' 제어신호를 보내면
2. 메모리는 저장된 명령어를 CPU에게 건네주고
3. 이 명령어는 레지스터에 저장된다.
4. 그러면 제어장치가 레지스터에 있는 이 명령어를 해석하고
5. 필요한 데이터가 있다면 제어장치가 또 신호를 주는 식으로 동작한다.
- 시스템 버스는 제어 버스, 주소 버스, 데이터 버스가 존재. 프로그램을 처리할 때 각 버스에서 제어신호, 주소, 데이터를 주고받는다.
2장
이진수와 컴퓨터의 작업처리
- 컴퓨터는 이진수밖에 알아듣지 못함. 우리가 처리하는 모든 데이터는 0과 1로 구성
- 아래첨자로 (2)를 붙이거나, 숫자 앞에 0b를 붙여 표현 (앞은 대수적, 뒤는 코드적 표현)
- 010101 신호가 비트, 8비트가 1바이트. 1000바이트가 1kB, 1000kB가 1MB
- 관습적으로 1MB를 1024kB, 1GB를 1024MB로 표현하는 것은 잘못됨. 1024로 묶으려면 kiB, MiB로 표현해야 함
늘 헷갈리던 2의 보수
2의 보수는 '그 수보다 더 큰 2의 거듭제곱에서 그 수를 빼는 것'
-> 다시 말하면, '모든 0과 1을 뒤집고(~(틸드)연산자를 붙이고) 거기에 1을 더한 값'
ex) 110을 음수로 표현하려면
- 1000 에서 빼면 되므로 010
- 모든 0과 1을 뒤집으면 001, 1을 더하면 010
2의 보수도 모든 걸 다 표현할 수는 없다
- 0000을 2의 보수법 쓰면 1111 -> +1 하면 10000? 0이 아닌 수가 나온다
- 100을 2의 보수법 쓰면 011 -> +1 하면 100? 나 자신으로 돌아온다
-> 0, 2의 거듭제곱은 음수로 표현할 수 없다
(책은 이것을 'n비트로는 -2^n과 2^n을 동시에 표현할 수 없다' 고 했다)
이진수는 너무 길어 - 16진수의 등장
- 0~9, A~F로 구성 (10~16이 A~F)
- 아래첨자 (16)을 붙이거나, 숫자 앞에 0x를 붙여 표현 (앞은 대수적, 뒤는 코드적 표현)
- 2진수에 비해 적은 자릿수로 더 크고 많은 수 표현 가능
- 2진수 ↔ 16진수 간 전환이 편리 (16진수는 2진수 4비트니까, 4비트짜리 이진수 덩어리로 보면 간편)
0과 1로 문자를 표현하려면 - 인코딩과 디코딩
- 인코딩은 문자를 비트로 전환하여 컴퓨터를 이해시키는 것
- 디코딩은 비트를 문자로 전환하여 사람이 이해할 수 있게 표현하는 것
- 종류: ASCII, EUC-KR, 유니코드, UTF-8 등
ASCII
- 아스키 문자는 7비트로 표현
-> 실제로는 8비트를 모두 사용하지만, 개중 1비트는 패리티(parity) 비트로 오류검출용이고 문자표현용은 아님
- 0~127까지 총 128개의 문자 존재 (2^7)
- 문자가 숫자에 대응 (ex. 'A'는 65) -> 코드 포인트 라고 함
- 장점: 매우 간단하게 인코딩
- 단점: 한글 표현 불가, 아스키 외의 다른 문자, 특수문자도 표현불가
EUC-KR
- 이 인코딩 방식을 알기 위해서는 한글의 특수성부터 짚고 가야함 -> 일본어, 영어처럼 나열식이 아니라 조립식 문자
TMI) 인코딩 고난이도가 한국어와 베트남어. 딩벳이 괜히 나온게 아님 (?)
- 초성 + 중성 + 종성의 합인 완성형 글자에 코드를 부여하는 완성형 방식, 각 초성 중성 종성에 비트열을 부여하는 조합형 방식이 존재
- EUC-KR은 완성형 인코딩의 일종 - 그러나 EUC-KR로도 모든 한글을 표현할 수는 없음
(뷁뛝쒥꿻 같은거 코드할당 안 돼있어서 깨짐)
유니코드와 UTF-8
- EUC-KR처럼 각 언어별 인코딩이 아닌, 모든 문자와 모든 언어 인코딩을 지원하는 아주 중요한 인코딩 방식
- UTF-8, UTF-16 등은 유니코드의 인코딩 방식을 다르게 하는 것
- 개중 가장 대중적인 방식은 UTF-8
3장
컴퓨터는 기계어와 어셈블리어밖에 몰라요
- 컴퓨터를 작동시키려면 명령어가 필요
- 코드를 작동시켜도 컴퓨터는 잘 돌아간다 -> 코드를 명령어로 바꿔주는 것
- 컴퓨터가 이해할 수 있는 저급언어, 사람이 이해할 수 있는 고급언어가 존재
저급언어는 기계어(0과 1)와 어셈블리어, 이 외는 모두 고급언어
- 어셈블리어는 기계어보단 쉽고 강타입 언어보단 어려움
-> 어떤 개발자가 될 것이냐에 따라 필요성이 다름. 게임개발 하려면 필수
컴파일과 인터프리트
- 컴파일은 소스코드를 통으로 저급언어로 번역하여 실행하는 것
그래서 구문에 오류가 하나라도 있으면 컴파일에 실패하고 소스코드는 실행되지 않음
- 인터프리트는 한 줄씩 번역하여 실행하는 것
그래서 예를 들어 53번 라인에 오류가 있다면 52번 라인까지는 제대로 실행
- 일반적으로 강타입 언어는 컴파일 언어, JS와 파이썬은 인터프리터 언어로 생각하지만, 강타입도 인터프리터를 쓰고 파이썬도 컴파일을 함
그러면 그 컴퓨터가 이해한다는 명령어는...
- 명령어는 연산코드와 오퍼랜드로 구성
-> 연산코드는 명령어가 수행할 연산, 오퍼랜드는 그 연산에 사용할 데이터 혹은 데이터가 들은 주소값
- 연산코드가 담기는 곳은 연산코드 필드, 오퍼랜드가 담기는 곳은 오퍼랜드 필드라고 부름
그중 오퍼랜드는
오퍼랜드는 값 자체(리터럴) 보다는 값이 있는 주소를 담은 경우가 많아서 주소 필드 라고도 부름
- 오퍼랜드는 연산에 따라 없을 수도 있어서, 갯수에 따라 0~3-주소 명령어 라고 명명
(오퍼랜드가 없으면 0-주소 명령어, 3개면 3-주소 명령어)
그러면 연산코드는
- 크게 네 가지로 구분
데이터 전송, 산술/논리 연산, 제어 흐름 변경, 입출력 제어
왜 값이 아니라 주소를 담나요? 주소 지정 방식
- 명령어 길이 때문.
명령어는 연산코드 + 오퍼랜드 인데, 보통 연산코드 부분이 길기 때문에 오퍼랜드를 가장 많이 할당할 수 있는 1-주소 명령어여도 오퍼랜드가 받아갈 수 있는 길이는 많지 않다. 2, 3-주소면 더 길어질 것.
- 이때 연산 대상이 되는 데이터가 담긴 주소를 유효 주소 라고 함
- 이렇게 데이터의 위치를 찾는 방식을 주소 지정 방식 이라고 함
이 주소 지정 방식은 대표적으로 다섯 가지가 있음
- 즉시 주소 지정 방식
데이터 그 자체를 오퍼랜드 필드에 직접 명시하는 방식
-> 가장 간단하고 빠르지만 표현 데이터 크기가 작다
- 직접 주소 지정 방식
유효주소를 오퍼랜드 필드에 직접 명시하는 방식
-> 즉시 주소 방식보다는 표현 데이터 크기가 크지만 여전히 작아 유효주소에 제한
- 간접 주소 지정 방식
'유효주소의 주소'를 오퍼랜드 필드에 직접 명시하는 방식
-> 앞선 방식들보다 일반적으로 느리지만 표현 데이터 크기가 크다
- 레지스터 주소 지정 방식
직접 주소 방식과 비슷하게 레지스터를 오퍼랜드 필드에 직접 명시. 더 빠름
-> 그러나 직접 방식과 비슷하게 표현 크기에 제한
- 레지스터 간접 주소 지정 방식
연산에 사용할 데이터를 메모리에 저장 후, 그 유효주소를 저장한 레지스터를 오퍼랜드 필드에 명시
간접 주소 방식과 비슷, 메모리 접근이 1회라는 장점
-> 메모리 접근이 레지스터 접근보다 느려서 (CPU 밖에 있기 때문에) 간접 주소 방식보다 빠르다
알아가기 - 스택과 큐
내가 아는 것은 스택은 LIFO, 큐는 FIFO 이라는 것이다. 우리가 보통 게임을 할때 게임 '큐'를 잡는다고 하지 게임 '스택'을 잡는다고 하지 않는다.
스택은 push, pop을 쓴다. github 명령어 중 git stash와 git stash pop이 대표적.