[C++] C++에서 사용되는 개념 1탄

Patrick!·2022년 5월 2일
0
post-thumbnail

1. 컴퓨터가 숫자(정수)를 저장하는 법

컴퓨터의 경우, 2진수, 8진수, 10진수, 16진수등 다양한 방법으로 데이터를 기억하고 있는데
이러한 기억 방법에 쓰이는 용량의 개념을 알아야 한다.

bitbyte는 컴퓨터가 기억할 수 있는 용량을 나타내는 말이다.

  • bit = 0과 1, 두가지 값만 가질 수 있는 측정 단위

  • byte = 8개의 비트로 구성된 데이터의 양을 나타내는 단위

해당 값들은 bit 와 byte의 크기에 따른 다른 의미를 정의한 것이다.
8 bit = 1 byte
16 bit = 2 byte = 1 word
32 bit = 4 byte = 2 word = 1 dword (double-word)
64 bit = 8 byte = 4 word = 1 qword (quad-word)

2. 컴퓨터가 임시적으로 데이터를 저장하는 개념

컴퓨터의 cpu가 연산 후, 연산 값을 어딘가에 두어야 한다. 하지만 이를 메인 메모리(Main Memory)에 담아두기에는 정말 말도 안되는 일이다. 그러다보니 cpu가 임시적으로 저장할 수 있는 공간이 필요한데 그 개념이 바로 레지스터(Register)이다. 실재로 cpu에서는 register와 연관된 작용이 많기에 cpu 와 register 는 영원한 단짝이다.

register의 경우, 복잡하게 다양한 용도로 많은 내용을 저장한다.
심지어 regiter 는 다양한 종류가 존재하나 지금은 EAX, EBX, ECX, EDX 만 집중해 보도록 해보자.

register를 사용하는 방법에는 bit의 개념이 사용된다.
비트를 나타내는 용어를 알아보자

RAX = 64bit / EAX = 32bit / AX = 16bit / AH, AL = 8bit / cl = 1bit

이를 코드로서 사용해보자

mov eax, 0x1234
mov rbx, 0x12345678
mov cl, 0xff

이렇게 register에 값을 저장한 수 있다. 하지만 여기서

mov cl, 0xfffffffffff //처럼 cl이 저장할 수 있는 값보다 큰 값을 저장하게 되면

warning: byte value exceeds bounds // 해당 값보다 크기에 저장할 수 없다고 반환 error 가 나온다.

3. 변수와 레지스터

매모리를 이용하는 방법에는 section .date / section .bss 를 이용하는 방법이 있다.

일종의 변수의 선언 및 사용
변수의 번언 방법
[변수이름] [크기] [초기값]
[크기] db(1) dw(2) dd(4) dq(8) 

db(1) dw(2) dd(4) dq(8) 해당 값들을 풀이하면

8 bit = 1 byte
16 bit = 2 byte = 1 word
32 bit = 4 byte = 2 word = 1 dword (double-word)
64 bit = 8 byte = 4 word = 1 qword (quad-word)

이를 줄여서 사용한 개념이다.
이제 사용하는 방법에 대해 알아보자

초기화 된 데이터
section .data
	[변수이름] [크기] [초기값]
	a db 0x11;  이를 실행하면 변수에 [0x11]이라는 값이 들어가게 된다.
    b dw 0x2222;
    c dd 0x33333333;
    d dq 0x4444444444444444;
    
초기화 되지 않은 데이터
    [변수이름] [크기] [개수]
    [크기] resb(1) resw(2) resd(4) resq(8)
section .bss
	e resb 10

그럼 여기서 궁금한 점이 생기기 마련인데 ...

도대체 .data.bss 의 차이점은 무엇인가 ?
.data = 초기화 된 데이터
.bss = 초기화 되지 않은 데이터 의 차이만 존재한다.

그런데 왜 굳이 두개로 나누었을까 ?

exe 파일 구조를 보면 Section(.data) 구조가 존재한다
.data(초기화 된 데이터)의 경우 파일의 어딘가에는 저장이 되어 있어야 하기 때문이다.
.bss(초기화 되지 않은 데이터)의 경우, 처음에는 모든 값들이 0으로 세팅이 된다.
최종적인 값을 알고 있다면 굳이 파일에 어떤 값을 세팅해야 하는지 필요로 하지 않기에 exe파일의 최종적인 크기가 줄어든다는 장점 때문이다.
(즉, 파일의 크기때문에 비록된 것이라고 보면 된다.)

메모리 <-> 레지스터 의 상호작용

mov eax, 0x1234
mov rbx, 0x12345678
mov cl, 0xff 
이들을 배웠기에 이를 응용할 수 있지 않을까?

mov rax, a // 이렇게 선언한다면 a 를 rax 라는 레지스터에 넣어준다

이는 rax 레지스터에 0x403010 이라는 값이 들어가고 이를 분석하면 4206608 이라는 값이 들어가 있음을 확인할 수 있다. a 에서 선언한 값과는 전혀 다른 값으로 나온다.

하지만 이 타입을 Hex(16진수)로 표현하고 [address] 를 체크하면
해당 값에는 a 에서 선언한 값들이 들어가 있음을 확인할 수 있다.

즉, ras안에 있던 원래의 값은 [주소값(0x403010)] 을 가져왔건 것이다!
여기서?! 주소값이란?
메모리에 어떤 데이터가 들어갈 때, 그 값을 저장한 주소값이 저장하는 개념으로 작동하기에 메모리의 주소값으로 처음에는 표기가 되는 것을 알 수 있다.

메모리에 어떤 데이터가 올라갈 때는 해당 메모리의 주소값이 있는데 해당 메모리의 bit 단위로 이동한다면 그 메모리에 어떤 값이 있는지를 알 수 있다.

이제 메모리에 어떤식으로 저장이 되는지 알았다면 메모리 -> 레지스터 의 개념을 적용해보자

이제 뭔가 정상적으로 복사가 된거 같지만 아까와는 다른 주소값이 출력된다.
mov rax, [a] // a라는 변수를 rax에다가 복사

a = 0x4433333333222211 이렇게 주소값이 출력이 된다. 
즉, 얼마만큼의 크기를 정의하지 않았기 때문에 아까와는 다른 주소값이 나오고 있는거다

여기서 크기를 이용해 선언해보자
mov al, [a] 
a = 0x1211 이상태에서는 정상적으로 출럭되는 것을 볼 수 있다.

반대로 레지스터 -> 메모리 의 개념을 알아보자

mov [a], 0x55 라고 선언하면 되지만 상수값을 넣기 때문에 크기를 정의해야 한다.

mov [a], byte 0x55 이렇게 크기를 선언해줘야 한다.
mov [a], word 0x6666
mov [a], cl 이는 아까와 다르게 크기를 선언하지 않아도 되는 이유는 [cl] 자체가 크기를 나타내고 있기 때문이다.
profile
C++와 Unreal Engine / C#과 Unity / Katalon Studio를 통한 자동화 테스트 등을 하루하루 공부한 기록

0개의 댓글