동적메모리 할당
동적메모리 할당의 개념
일반적으로 c에서 배열의 경우 사전에 적절한 크기만큼 할당해주어야 한다.
우리가 원하는 만큼만 메모리를 할당해서 사용하고자 한다면 동적메모리 할당을 사용한다.
동적이라는 말의 의미는 '프로그램 실행 도중에'라는 의미이다.
c에서는 malloc()을 이용해 원하는 만큼의 메모리 공간을 확보할 수 있는데 malloc()은 메모리할당에 성공하면
주소를 반환하고 그렇지 않으면 null을 반환한다.
malloc()은 <stdlib.h>에 정의되어 있다.
malloc(할당할 바이트 크기) 형태로 사용하며 동적메모리 할당을 수행할 때마다 할당되는 포인터의 주소는 변칙적이다.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a = malloc(sizeof(int));
printf("%d\n", a);
a = malloc(sizeof(int));
printf("%d\n", a);
system("pause");
return 0;
}
sizeof(int)는 4바이트가 반환되는데 위 코드에서는 malloc(4)와 같게 된다.
할당에 성공했다면 포인터변수 a에 들어가고 실패했다면 null값이 들어가게 된다.
실행해보면 서로 다른값이 출력되게 되며 실행시마다 값이 달라진다.
인강에서는 두번째 a를 *a로 만들어 주어도 다른값이 나온다고 했지만 여러번 해봤지만 계속 동일한 값이 출력되었다.
동적으로 할당된 변수는 힙 영역에 저장되며 기본적인 C언어에서는 스택에 선언된 변수는 따로 메모리 해제를 해주지
않아도 된다고 한다. 반면에 동적으로 할당된 변수는 반드시 free()로 메모리 해제를 해주어야 한다.
메모리 해제를 하지 않으면 메모리내의 프로세스 무게가 더해져 언젠가는 오류가 발생하게 된다.
프로세스의 무게가 더해진다는 것은 메모리가 계속 중첩되어 사용공간이 증가하고 사용가능한 공간이 줄어든다는
것이다. 메모리누수(Memory Leak)방지는 코어 개발자의 핵심역량이라고 한다.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *a = malloc(sizeof(int));
printf("%d\n", a);
free(a);
a = malloc(sizeof(int));
printf("%d\n", a);
free(a);
system("pause");
return 0;
}
이런 형태로 사용하면 된다
malloc()과 free()는 한쌍으로 무조건 붙는다고 생각하면 된다고 한다.
이렇게 작성하게 되면 같은값으로 출력되는 것을 확인할 수 있는데
처음 할당받은 주소를 받은 뒤에 free()로 메모리를 비워주고 다시 할당받게 되니 처음 할당받았던 주소를 받을 확률이
높아졌기 때문이다. 거의 100%에 가까울 정도로 높은 확률로 같은값을 받게 된다고 한다.
동적으로 문자열 처리
일괄적인 범위의 메모리를 모두 특정한 값으로 설정하기 위해서는 memset을 사용하며
memset(포인터, 값, 크기); 형태로 사용한다.
한 byte씩 값을 저장하므로 문자열 배열의 처리방식과 흡사하다.
그래서 memset()은 <string.h>에 정의되어 있다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *a = malloc(100);
memset(a, 'A', 100);
for(int i = 0; i < 100; i++){
printf("%c ", a[i]);
}
system("pause");
return 0;
}
위 예제에서는 malloc(100)으로 100만큼의 공간을 할당한 뒤에 memset(a, 'A', 100)으로 a배열에 'A'를 100개 채우게
된다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int **p = (int**)malloc(sizeof(int*) * 3);
//**는 이중포인터.
//즉 2차원 배열 생성 후 3개의 행을 할당.
for(int i = 0; i < 3; i++){
*(p + i) = (int*)malloc(sizeof(int) * 3);
//각각의 행에 들어갈 열을 3개씩 할당.
}
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
*(*(p + i) + j) = i * 3 + j;
//*(*(0) + 0) = 0 * 3 + 0 = 0
//*(*(0) + 1) = 0 * 3 + 1 = 1
//*(*(0) + 2) = 0 * 3 + 2 = 2
//*(*(1) + 0) = 1 * 3 + 0 = 3
//*(*(1) + 1) = 1 * 3 + 1 = 4
//*(*(1) + 2) = 1 * 3 + 2 = 5
//*(*(2) + 0) = 2 * 3 + 0 = 6
//*(*(2) + 1) = 2 * 3 + 1 = 7
//*(*(2) + 2) = 2 * 3 + 2 = 8
}
}//각 행, 열에 값을 입력
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
printf("%d ", *(*(p + i) + j));
}//출력
printf("\n");
}
system("pause");
return 0;
}
위 예제는
0 1 2
3 4 5
6 7 8 의 결과값을 출력한다.
**는 이중포인터 이며 2차원 배열을 생성한 후에 3개의 행만큼 들어갈 수 있도록 만들어준 것이다.
하나의 포인터가 한개의 1차원 배열을 처리할 수 있기 때문에 sizeof(int*)가 1차원 배열의 시작점을 가리키는
포인터가 된다. 그리고 3을 곱해줌으로써 총 3개의 행이 만들어지는 것이다.
그리고 첫 for문에서 각각의 행에 들어갈 열을 3개씩 할당해준다.
*(p + i) 는 각각의 행을 의미하는데 각 행마다 3개의 열을 추가해주는 것이다.
그다음 이중for문에서는 각각의 행과 열에 값을 입력하게 되는데
*(p + i)는 행 j는 열을 의미한다.
마지막 for문은 출력문이다. 마찬가지로 이차원배열이기 때문에 이중 for문으로 출력해준다.
이 예제에서는 free를 사용해서 메모리해제를 하지 않았는데 한번 전체적으로 실행되고 나면 종료되는 코드이기
때문에 굳이 사용하지 않은것이지만 만약 장시간 실행되며 처리하는 코드라면 꼭 free()를 사용해서 메모리를
해제해주어야 한다.
레퍼런스
패스트캠퍼스 올인원 패키지 - 소프트웨어 베이직