#12.2 동적 메모리 사용 절차(2)
🏃♂️ 동적 메모리 할당 함수 사용법을 익힌다.
동적 메모리 해제: free() 함수
지정된 메모리 공간을 해제(free)
함수 원형 | void free(void *ptr); |
함수 인자 | 해제할 메모리 공간을 가리키는 포인터 변수 |
기능 | ptr이 가리키는 메모리 해제 ※ ptr 변수 자체를 해제시키는 것이 아니다. |
ex.
int *p = NULL;
p = (int *)malloc(10*sizeof(int));
free(p); // p가 가리키는 영역 해제
ex.
1. 정수 한 개를 저장할 수 있는 메모리 공간 할당하고 해제하기
2. float형 실수 한 개를 저장할 수 있는 메모리 공간 할당하고 해제하기
3. double형 실수 15개를 저장할 수 있는 메모리 공간 할당하고 해제하기
4. 다음과 같은 구조체 한 개를 저장할 수 있는 메모리 공간 할당하고 해제하기
struct student{
int id;
char name[8];
double grade;
}
int main() {
// 1.
int *ip=(int*)malloc(1*sizeof(int));
free(ip);
// 2.
float *fp=(float*)malloc(1*sizeof(float));
free(fp);
// 3.
double *dp=(double*)malloc(15*sizeof(double));
free(dp);
// 4.
struct student *sp = (struct student *)malloc(1*sizeof(struct student));
free(sp);
}
동적 메모리를 안전하게 사용하기 위한 주의사항
- malloc()의 반환 값을 검사하여 메모리 할당의 성공 여부 확인
- NULL 포인터 반환하는 경우: 요청한 메모리 크기만큼 연속된 메모리 공간을 확보하지 못할 때
- 비슷하게, 해제하려는 메모리 주소가 NULL인지 검사
int *p = NULL;
p = (int *)malloc(10*sizeof(int));
if(p==NULL) { // 메모리 할당 실패하면
printf("Not enough memory!"); // 오류 상황을 알리고
return -1; // 함수 종료
}
if(p!=NULL) // p가 NULL이 아닌 경우에만
free(p); // free() 함수 호출
메모리 할당 방식 비교
정적 메모리 할당 | 동적 메모리 할당 | |
필요한 메모리 크기 결정시점 | 프로그램 작성 단계 | 프로그램 실행 중 |
메모리 할당 및 해제 시점 | 시스템이 자동으로 할당 및 해제 | 개발자가 명시적으로 할당 및 해제 함수 호출 |
프로그램 실행 중 메모리 크기 변경 여부 | 불가 | 가능 |
메모리 누수(leak) 가능성 (할당 받은 메모리를 안 돌려주는 것) |
없음 | 있음 |
메모리 사용의 효율성 | 비효율적 | 효율적 |
※ 만약 동적 메모리 할당을 한 후 해제(free())를 하지 않을 경우, 프로그램 종료 시 변수만 사라지고, 메모리 안에는 그대로 저장되어 있다.
※ 그러나 일반적인 프로그램은 프로그램 종료 시 메모리 해제된다. 서버 같은 경우에는 메모리 누수가 일어날 수 있다.
#12.3 동적 메모리 사용 예제(1)
🏃♂️ 예제를 통해 동적 메모리 할당 사용법을 익힌다.
1) 동적으로 메모리를 할당 받아 사용하는 기본 프로그램
하나의 정수를 저장하는 메모리를 동적 할당 받아서 사용(실용성은 없음)
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = NULL; // 포인터 변수 선언
p = (int *)malloc(sizeof(int)); // 동적 할당
if (p == NULL) { // 동적 할당 오류 검사
printf("Not enough memory!");
return -1;
}
*p = 15; // 동적 할당 메모리에 값 저장
printf("동적 메모리 할당 (정수형): %d", *p);
free(p):
return 0;
}
2) 일차원 배열을 동적 할당 받아 사용하는 프로그램
- 여러 개의 정수를 저장하는 메모리를 동적 할당 받아 사용
- 문제: 한 학생의 과목별 점수를 입력 받아 평균 점수를 구한 후 출력하는 프로그램을 작성하시오.
- 프로그램 실행 중에 사용자로부터 과목 수 n을 직접 입력 받음.
- n개의 정수형 값을 저장할 수 있는 메모리를 동적으로 할당 받아, 할당 받은 메모리의 시작 주소를 score에 저장
- 할당 받은 메모리를 score[0]~score[n-1]까지 일반 배열 원소처럼 참조하여 사용
- 할당 받은 메모리 해제
#include <stdio.h>
#include <stdlib.h>
int main() {
int n, i, sum = 0;
int *score = NULL;
scanf("%d", &n); // 1. 과목 수 입력받기
score = (int *)malloc(n*sizeof(int)); // 2. 배열 동적 할당
if (score == NULL) { // 동적 할당 오류 검사
....
}
for (i = 0; i<n; i++) {
scanf("%d", &score[i]); // 3. 과목별 점수 입력받기
sum += score[i]; // 누적 합 계산
}
printf("%.1f", (double)sum/n); // 평균 계산
free(score); // 4. 동적 할당 메모리 해제
return 0;
}
학습 정리
- free() 함수는 할당된 메모리를 해제하여 시스템에 되돌려 준다.
- 정적 할당은 할당 메모리가 코드 작성 단계에서 정해지고, 프로그램을 실행할 때마다 동일한 크기로 할당하는 방식
- 동적 할당은 프로그램 실행 단계에서 결정되는 크기에 따라 메모리를 할당하는 방식
- 동적 할당과 포인터를 활용하여 일차원 배열을 사용할 수 있다.(예제 2)
#12.3 동적 메모리 사용 예제(2)
3) 동적 메모리 할당을 사용한 문자열 처리 프로그램
길이가 다른 여러 개의 문자열을 효율적으로 저장하기 위해 다음과 같은 방식으로 동적 메모리 할당 이용
- 문자열을 입력 받기 위한 충분한 크기의 문자 배열을 선언(정적 할당)
- 1.의 문자 배열에 문자열을 입력 받는다.
- 2.에서 입력된 문자열의 길이를 계산하여 그 크기에 맞게 메모리를 동적으로 할당 받는다.(널 문자 포함)
- 할당 받은 메모리 공간에 1.의 문자 배열의 문자열을 복사한다.
문제: 사용자로부터 책의 개수 n을 입력 받아, n개의 책 제목을 저장하는 프로그램을 작성하시오.
- 사용할 메모리 구조
- 책 하나의 정보를 저장하는 구조체
-
typedef struct book_title { char *title; // 책 제목(포인터) ... // 기타 책 정보(필요 시) } BINFO;
- 책의 개수를 모르므로 동적 구조체 배열 사용
typedef struct book_title {
char *title; // 책 제목(포인터)
... // 기타 책 정보(필요 시)
} BINFO;
int main() {
BINFO *bp = NULL;
int n, i, len;
char temp[100]; // 1. 문자열을 입력 받기 위한 문자 배열을 선언
scanf("%d", &n); // 책 개수 입력
getchar(); // 개행 문자 버리기
// 책 배열 동적 할당 및 동적 할당 오류 검사
bp = (BINFO *)malloc(n*sizeof(BINFO));
if (bp == NULL) { ... }
for (i=0; i<n; i++) {
gets(tmp); // 2. 문자 배열 temp에 문자열을 입력 받음
len = strlen(temp); // 3. temp에 입력된 문자열 길이를 계산하고,
bp[i].title = (char *)malloc((len+1)*sizeof(char)); // 3. 그 크기만큼 메모리를 동적으로 할당 받음(널 문자 포함)
if(bp[i].title == NULLL) { ... } // 동적 할당 오류 검사
strcpy(bp[i].title, temp); // 4. 할당 받은 메모리에 제목 복사
}
for (i=0; i<n; i++)
printf("%s\n", bp[i].title); // 책 제목 출력
for (i=0; i<n; i++)
free(bp[i].title); // a. 책 제목(문자 배열)에 대한 메모리 해제
free(bp); // b. 책 배열(구조체 배열)에 대한 메모리 해제
return 0;
}
해제할 때 순서(a->b)도 중요하다. 순서 틀리면 접근 불가해질 수 있다.
4) 동적 메모리 할당을 사용하여 2차원 배열을 할당하는 프로그램
- 문제: char array[3][4]라는 2차원 배열을 동적으로 할당하기
- 착각하기 쉬운 잘못된 방법
- char *pch = (char *)malloc(3*4*sizeof(char)); -> 사실 상 malloc(12); 호출이다.
- 이건 2차원 배열 할당이 아니라, 크기가 12인 1차원 배열 할당이다.
- 해결방법: 포인터 배열 사용
- 다른 다차원 배열도 동일한 원리 적용: 2차원 정수 배열, 3차원 배열 등
※ 정적 할당에서의 2차원 배열과 동적 할당에서의 2차원 배열은 메모리 구조가 완전히 다르다. 그러나, 코드 상으로는 2차원 배열처럼 사용할 수 있다. (cf. 포인터 배열)
#include <stdio.h>
#include <stdlib.h>
int main() {
int i;
char **pch; // 이중 포인터 선언
pch = (char **)malloc(3*sizeof(char *)); // 포인터 배열 할당
for (i=0; i<3; i++)
pch[i] = (char *)malloc(4*sizeof(char)); // 1차원 배열 할당
strcpy(pch[0],"aaa");
strcpy(pch[1],"bbb");
pch[2][0] = '\0'; // 2차원 배열처럼 사용(예제 9.10 참조)
for (i=0; i<3; i++)
puts(pch[i]);
for (i=0; i<3; i++)
free(pch[i]); // 1차원 배열 메모리 해제
free(pch); // 포인터 배열 메모리 해제
return 0;
}
#12.4 기타 동적 메모리 할당 함수
calloc() 함수
함수 원형 | void * calloc(unsigned int num, unsigned int size); | |
함수 인자 | num | 동적으로 할당 받을 원소의 개수 |
size | 원소 한 개의 크기 (바이트 단위) | |
반환 값 |
|
malloc()과 거의 비슷하나 인자가 다르다. 그리고 0으로 초기화 한다.
int *p = NULL;
p = (int *) calloc(5, sizeof(int)); 크기 5인 int형 배열 할당. malloc(5*sizeof(int));
realloc() 함수
어떨 때 쓰는 건지 모르겠음
함수 원형 | void * realloc(void *ptr, unsigned int size); | |
함수 인자 | ptr | 확장 크기를 변경할 메모리의 시작 주소 |
new_size | 변경 후 메모리의 전체 크기(바이트 단위) | |
반환 값 |
|
char *p = (char *)malloc(5); // 5 바이트 만큼 공간 할당
p = (char *)realloc(p,10); // 10 바이트로 공간 크기 변경
※ 기존 위치에서 새로운 크기를 확보할 수 없으면, 기존 위치의 공간을 해제하고 새로운 위치에 공간 할당
학습 정리
- 동적 할당을 다단계로 적용하여 복잡한 구조의 동적 메모리를 사용할 수 있다.(예3)
- 2차원 동적 배열은 포인터 배열을 사용하여 두 단계의 동적 할당으로 표현한다.(예4)
- calloc() 함수는 malloc() 함수와 유사하게 메모리를 할당해 주는데, 추가로 할당 받은 메모리 공간을 0으로 초기화 한다.
- realloc() 함수는 할당 받은 메모리 공간의 크기를 변경하고자 할 때 사용한다.