프랙티스만이 살길. 프랙티스만이 살길.
5. 메모리 주소 본문
- 16진수
- 컴퓨터과학에서는 숫자를 10진수나 2진수 대신 **16진수(Hexadecimal)**로 표현하는 경우가 많습다. 컴퓨터에서 데이터를 처리하기 위해 16진수를 사용할 때 장점이 있기 때문이다.
- 2개의 16진수는 1byte의 2진수로 변환되기 때문에 정보를 표현하기 매우 유용하다.
- 메모리 주소
- 정수형 변수 n에 50이라는 값을 저장하고 출력한다고 생각해 보자. 이 n 이라는 값은 int 타입이므로, 컴퓨터의 메모리 어딘가에 4바이트 만큼의 자리를 차지하며 저장되어 있다. C에서는 변수의 메모리상 주소를 받기 위해 '&’이라는 연산자를 사용할 수 있다.
#include <stdio.h>
int main(void)
{
int n = 50;
printf("%p\\n", &n);
}
위와 같은 코드를 실행하면 ‘0x7ffe00b3adbc’와 같은 값을 얻을 수 있고, 이는 변수 n의 16진법으로 표현된 메모리의 주소이다.
%p는 주소를 출력해주는 형식지정자이다.
#include <stdio.h>
int main(void)
{
int n = 50;
printf("%i\\n", *&n);
}
반대로 ‘*’를 사용하면 그 메모리 주소에 있는 실제 값을 얻을 수 있다. 위 코드는 먼저 n의 주소를 얻고, 또 다시 그 주소에 해당하는 값을 얻어와 출력한 것이므로 결국 ‘50’이라는 값이 출력된다.
- 포인터
#include <stdio.h>
int main(void)
{
int n = 50;
int *p = &n;
printf("%p\\n", p);
printf("%i\\n", *p);
}
위 코드를 보면 정수형 변수 n에는 50이라는 값이 저장되어 있다.
어떤 변수에 주소를 저장하고 싶다면 별연산자를 붙여 포인터로 만들어줘야한다.
*p라는 포인터 변수에 &n 이라는 값, 즉 변수 n의 주소를 저장한다. int *p 에서 p앞의 *는 이 변수가 포인터라는 의미이고, int 는 이 포인터가 int 타입의 변수를 가리킨다는 의미이다.
따라서 첫 번째 printf문과 같이 포인터 p의 값, 즉 변수 n의 주소를 출력하거나, 두 번째 printft문과 같이 포인터 p가 가리키는 변수의 값, 즉 변수 n의 값을 출력할 수도 있다.
실제로 p의 값, 즉 n의 주소값을 생각하지 않고, 추상적으로 단지 p가 n을 가리키고 있다는 것 만 생각해도 된다. 최신컴퓨터는 64bits포인터로 사용한다.
- 문자열과 포인터
- 여태껏 문자열을 저장하기 위해 CS50 라이브러리에 포함된 string 자료형을 사용하였다. s에 “EMMA”라는 값을 저장한다고 할 때.
string s = “EMMA”;
문자열은 결국 문자의 배열이고, s[0], s[1], s[2], … 와 같이 하나의 문자가 배열의 한 부분을 나타낸다.
변수 s는 문자열의 가장 첫번째 문자 E를 가리키는 포인터가 된다.
#include <stdio.h>
int main(void)
{
char *s = "EMMA";
printf("%s\\n", s);
printf("%p\\n", s);
printf("%p\\n", &s[0]);
}
엠마라는 문자열이 어딘가에 저장되어 있고, s는 그것의 첫글자 주소이다. %s(문자열 지정자)를 사용했기 때문에, 종단문자가 나올때까지 메모리를 읽어 문자열이 출력된다. 즉 형식 지정자는, 변수를 찾았을때 언제까지 메모리를 읽을건지에 대한 명령어다(ex %i라면 4바이트, %id라면 8바이트 %s라면 종단문자가 나올때까지….) 순서대로 문자열, 첫번째 문자의 주소, 첫번째 문자의 주소가 출력된다.
- malloc
- malloc은 메모리 할당 함수로 정해진 크기만큼의 메모리를 할당한다.
- free
- malloc 함수를 이용하여 메모리를 할당한 후에는 free라는 함수를 이용하여 메모리를 해제해줘야 한다. 그렇지 않은 경우 메모리에 저장한 값은 쓰레기 값으로 남게 되어 메모리 용량의 낭비가 발생하게 되기 때문이다. 이러한 현상을 **‘메모리 누수’**라고 부른다.
- 메모리 구역
메모리 안에는 데이터 저장되는 구역이 나뉘어져 있습니다. 머신 코드 영역에는 우리 프로그램이 실행될 때 그 프로그램이 컴파일된 바이너리가 저장됩니다. 글로벌 영역에는 프로그램 안에서 저장된 전역 변수가 저장됩니다. 힙 영역에는 malloc으로 할당된 메모리의 데이터가 저장됩니다. 스택에는 프로그램 내의 함수와 관련된 것들이 저장됩니다.