
2025.04.05
WEEK04 :
동적 프로그래밍, 그리디 알고리즘
CSAPP 3장. 프로그램의 기계 수준 표현 (특히 3.4, 3.7, 3.8)
3.4 ~ 3.5 까지 공부
목표는 3.6 까지 였는데 생각보다 내용이 엄청많다.. 부랴 부랴 하는중!
명령어는 16개 레지스터의 낮은 정렬에 있는 바이트에 저장되어 있는 다른 사이즈의 데이터에 대해 연산한다. 바이트 수준 연산은 최하위 비트에 접근한다. (16-비트 연산은 최하위 2바이트, 32비트는 4비트, 64-비트는 모든 레지스터에 접근한다.)
%rax,%rbp,%rsp,%r8~%r15이런 식
%rax ← 전체%eax = 하위 32비트%ax = 하위 16비트%al = 하위 8비트%rsp이며, 런타임 스택에서 마지막 위치를 가리키는 데 사용된다. 어떤 명령어는 이 레지스터를 읽거나 쓸 때가 있다. 다른 15개의 레지스터들은 그들의 사용에서 유연함을 가진다.정리
CPU에는 16개의 64비트 레지스터가 있고, 그 일부만 쓸 수도 있음.
%rsp는 스택의 꼭대기를 가리키며, 레지스터 사용은 프로그래밍 규약에 따라 정해져 있음.

| 용어 | 뜻 |
|---|---|
| 피연산자(Operand) | 명령어가 연산할 "값" 또는 "주소" |
| 소스(Source) | 값을 가져오는 곳 |
| 목적지(Destination) | 결과를 저장하는 곳 |
Immediate (즉시 값)
$5, $0x10 (앞에 $ 붙임)Register (레지스터)
%rax, %rbx)Memory (메모리 참조)
Imm(rb, ri, s)offset(%base, %index, scale)주소 = 기준 주소 + 인덱스 * 크기

데이터를 "다른 위치로 복사"한다는 건, 메모리 접근 또는 레지스터 조작이 필요하다는 뜻 → 무거운 명령어
메모리 ↔ 메모리, 메모리 ↔ 레지스터 간 복사
MOV 계열 명령어는 “데이터 이동만 수행”하는 명령어 그룹
- 소스 → 목적지 복사
- 연산 X, 조건 X, 단순히 "값을 복사"만 함
- 값을 한 위치에서 다른 위치로 복사하는 것은 두 가지 명령어를 요구
- 소스 값을 레지스터에 로드(load)하는 것
- 목적지에 레지스터 값을 쓰는(write) 것
movz = Zero Extension→ 빈 부분을 0으로 채움 (양수 취급)
movzbq %al, %rax
movs = Sign Extension→ 빈 부분을 부호(양/음수) 에 따라 채움
movsbq %al, %rax
MOV 명령어는 단순 복사를 위한 핵심 명령어지만,
크기, 부호 확장, immediate 제한 등 예외가 많고 섬세한 규칙이 다수
movl은 레지스터 상위 0으로 채움movq는 64비트 즉시 값 제한됨movabsq는 큰 숫자도 OKmovz,movs는 크기 확장

%rsp는 현재 스택의 top 주소를 가리킨다%rsp는 항상 스택의 최상단(top) 을 가리킴pushq %rax
내부 동작:
%rsp를 8만큼 줄임 (%rsp = %rsp - 8)%rax 값을 저장→ 왜 8?
→ x86-64는 64비트(=8바이트) 환경이기 때문!
popq %rax
내부 동작:
%rsp가 가리키는 주소의 값을 %rax에 저장%rsp를 8만큼 증가시킴 (%rsp = %rsp + 8)✔️ 메모리 자체는 변하지 않음
→ 다만 %rsp가 옮겨가면서 더 이상 "스택의 top"이 아닌 상태가 되는 것!
→ 메모리에 여전히 남아 있다!
다만, %rsp가 다른 위치를 가리키기 때문에 "스택에서 제거된 것처럼" 보일 뿐
push를 하면 그 주소에 새 값이 덮어써질 수 있음연산자들은 4가지 그룹으로 나뉨:
| 그룹 | 예시 | 특징 |
|---|---|---|
| Load effective address | leaq | 산술 연산에 자주 사용됨 |
| Unary (1개의 피연산자) | inc, dec, neg, not | |
| Binary (2개의 피연산자) | add, sub, imul, xor, or, and | |
| Shift 연산 | shl, shr, sar, sal | 비트 단위 이동 |
leaq 명령어
leaq 7(%rdx,%rdx,4), %rax
⇒ %rdx + (4 * %rdx) + 7로 산술 계산이 가능
++,-- 와 비슷x -= y 와 비슷%rdx = %rdx - %rax (소스 먼저, 목적지 나중)| 명령어 | 의미 | 채우는 방식 |
|---|---|---|
shl / sal | 왼쪽 시프트 | 오른쪽에 0 채움 (동일한 동작) |
shr | 오른쪽 논리 시프트 | 왼쪽에 0 채움 |
sar | 오른쪽 산술 시프트 | 왼쪽에 부호 비트 복사 |
shl/sar/shr $<count>, <dest> # 상수값(count)만큼 (dest)를 시프트 shl/sar/shr %cl, <dest> # %cl (8비트 레지스터) 사용
salb (8비트): %cl의 하위 3비트 → 최대 7salw (16비트): 하위 4비트 → 최대 15sall (32비트): 하위 5비트 → 최대 31salq (64비트): 하위 6비트 → 최대 63시프트 횟수는 데이터 크기에 따라 제한됨
대부분의 인스트럭션들은 비부호형과 2의 보수 산술연산에 사용될 수 있다.
== 어셈블리 명령어 자체는 부호 여부를 따지지 않고 단순히 비트 계산만 한다는 뜻
컴퓨터는 비트 단위 계산만을 할 뿐mulq / imulq| 명령어 | 의미 | 부호 여부 |
|---|---|---|
mulq S | %rax × S → 128비트 저장 | unsigned |
imulq S | %rax × S → 128비트 저장 | signed |
반드시 %rax가 첫 번째 곱셈 피연산자여야 함
divq / idivq%rdx:%rax = 128비트 숫자(레지스터 두개 이어붙임)%rax%rdx| 명령어 | 설명 | 부호 |
|---|---|---|
idivq S | signed 나눗셈 | 부호 확장 필요 (cqto) |
divq S | unsigned 나눗셈 | %rdx를 0으로 초기화 필요 |
| 개념 | 설명 |
|---|---|
| 128비트 곱셈 | mulq(unsigned), imulq(signed), 결과는 %rdx:%rax |
| 128비트 나눗셈 | divq(unsigned), idivq(signed), 입력도 %rdx:%rax |
| 부호 확장 | cqto (signed), xor %rdx,%rdx (unsigned 초기화) |
| 레지스터 이름 | 의미 |
|---|---|
| rax | 함수 반환값 저장 |
| rdi,rds,rdx,rcx … | 함수 인자(파라미터) 저장 |
| rsp | stack top |
| rbp | stack base(데이터의 바닥) |
$10$10 : 즉시 주소 시정 : 바이트로 상수 바로 씀주소주소 : 직접 주소 지정 : 바로 주소 써놨다.movq %rax, %rbx : 레지스터 안에 값 바로movq %rax, (%rbx) : 레지스터 안에 주소의 값으로movq offset(%bp) , %rax : offset 만큼 이동해서 갖고와movq ARRAY(%bp) , %rax : 어셈블리 배열의 첫 시작 부분을 인덱스로movq ARRAY(%bp, offset) , %rax : 배열의 offset 부분부터 갖고 오겠다.백준 잔디는 매일 심어야 한다. CSAPP을 하니까 문제가 풀고싶더라.
import sys
input = sys.stdin.readline
n,k = map(int,input().split())
A = []
for _ in range(n):
A.append(int(input().strip()))
cnt = 0
for i in A[::-1]:
if k == 0:
break
if k < i:
continue
else:
cnt += k // i
k = k % i
print(cnt)
CSAPP 3장을 일주일 만에 다 읽고 이해하라는건 너무 빡세다. 오늘도 내일도 화이팅