728x90

1. 동적 할당 개요

1) 정적 메모리 할당
- 지금까지 기억 장소 확보를 위해 사용한 방식은 변수 선언
- 변수는 모두 코드 작성 단계에서 정해지고, 할당되는 메모리 크기는 프로그램을 실행할 때마다 일정
- 이는 프로그램 실행 전에 메모리 할당 크기가 정해지고, 메모리할당 도중 변하지 않음
- 정적 할당

2) 정적 할당 방식의 문제점
필요한 메모리 크기를 알 수 없는 경우, 충분히 큰 크기를 가정해서 선언해야 함
- 프로그램 실행 전, 메모리를 얼마나 필요로 할 것인지 정확하게 알 수 없는 경우, 
메모리를 효율적으로 사용하기 어려움
- 예시) 몇명의 사람이 회원가입을 할지 알 수 없음 >> 충분히 큰 크기 설정 
>> 회원수가 설정한 크기보다 작은 경우 >> 남은 메모리 낭비
>> 회원수가 설정한 크기보다 큰 경우 >> 배열 크기를 재조정 해야함

3) 동적 메모리 할당
프로그램을 실행하는 동안 결정되는 크기에 따라 메모리를 할당하는 방식
메모리를 효율적으로 사용할 수 있음
- 앞 웹사이트 상황에서, 크기가 5만인 메모리를 할당 받아 사용하다
>> 회원수가 설정한 크기보다 작은 경우 >> 적은 용량의 메모리를 새로이 할당 받아 사용
>> 회원수가 설정한 크기보다 큰 경우 >> 더 많은 용량 메모리를 새로이 할당 받아 사용
>> 기존 메모리 반납

2. 동적 메모리 사용 절차

1) 메모리 사용 절차
- 메모리 사용을 위해서는 메모리를 할당하고 해제하는 과정 필요
(1) 할당: 필요한 메모리를 시스템으로부터 받기
(2) 사용: 할당된 메모리 사용
(3) 해제: 사용이 끝난 메모리를 시스템에 반납

- 정적할당: 이 과정이 프로그램과 함수의 실행 및 종료에 따라 자동으로 수행됨
- 동적할당: 프로그래머가 필요한 과정을 코드에 명시적으로 작성해 주어야 함

2) 동적 할당 헤더파일: <stdlib.h>
- 동적으로 메모리를 할당하고 해제하는 함수는 <stdlib.h> 헤더파일에 존재

3) 동적 메모리 할당: malloc() 함수
<malloc() 함수>
- 요청된 연속된 메모리 할당(memory allocation)
(1) 함수 원형
void *malloc(unsigned int size);

(2) 함수 인자 
- 할당받을 메모리 크기(바이트 단위)
- 보통 sizeof() 연산자 활용

(3) 기능 및 반환값
할당 받은 메모리의 시작 주소를 반환
할당에 실패하면 >> NULL 반환
- 반환 값의 유형: void형 포인터

(4) 사용 예시

// 정수 2개를 저장할 공간 할당
malloc( 2*sizeof(int) );
malloc( 8 );


4) 동적 메모리 연결
- 변수 이름을 붙일 수 없기에, 포인터 변수를 이용하여 간접적으로 참조

(1) 포인터 변수 선언 >> 형변환으로 간접 참조
자료형 *포인터_변수명 = NULL;
포인터_변수명 = (자료형 *)malloc(메모리_크기);
(2) 포인터 변수 선인 및 참조
자료형 *포인터_변수명 = (자료형 *)malloc(메모리_크기);

>> 사용하고자 하는 자료형으로 명시적 형변화, 
할당된 메모리를 어떤 자료형으로 사용할 지 모르므로 (void *)형 변환

- 예시)

int *p = NULL;
p = (int *)malloc( 10*sizeof(int) );



5) 동적 메모리 사용
- 할당된 메모리를 포인터 변수에 연결한 후에는 포인터를 사용하는 방식으로 이용
- 예시)

int *p = (int *)malloc( 10*sizeof(int) );
p[0] = 1; // *p = 1; 과 동일
*(p+2) = 3; // p[2] = 3; 과 동일



6) 동적 메모리 해제: free() 함수
<free() 함수>
- 지정된 메모리 공간을 해제(free)
(1) 함수 원형
void free(void *ptr);

(2) 함수 인자
- 해제할 메모리 공간을 가리키는 포인터 변수

(3) 기능
ptr이 가리키는 메모리 해제
ptr 변수 자체를 해제시키는 것이 아님

7) 동적 메모리를 안전하게 사용하기 위한 주의사항
malloc()의 반환 값을 검사하여 메모리 할당의 성공여부 확인하기
- NULL 포인터 반환하는 경우: 요청한 메모리 크기만큼 연속된 메모리공간을 확보하지 못할 때
>> 오류상황 알리고, 함수 종료
- NULL 포인터 반환하지 않는 경우
>> free()함수 호출

8) 메모리 할당 방식 비교
<정적 메모리 할당>
프로그램 작성단계에서 메모리 크기 결정
시스템이 자동으로 메모리 할당 및 해제
- 프로그램 실행 중 메모리 크기 변경 불가
- 메모리 누수 가능성 없음
- 메모리 사용 비효율적

<동적 메모리 할당>
프로그램 실행 중에 메모리 크기 결정
- 개발자가 명시적으로 할당 및 해제함수 호출
- 프로그램 실행 중 메모리 크기 변경 가능
- 메모리 누수 가능성 있음
- 메모리 사용 효율적

3. 동적 메모리 할당 프로그램 예제

1) 동적으로 메모리를 할당 받아 사용하는 기본 프로그램
>> 정수 하나를 입력받을 메모리를 할당하고 저장하기(동적할당의 의미 없음)

#include<stdio.h>
#inlcude<stdlib.h>

int *p = (int *)malloc(sizeof(int));
if (p == NULL) return -1;

*p = 1;



2) 일차원 배열을 동적 할당 받아 사용하는 프로그램
>> 정수 배열을 입력받을 메모리를 할당하고 저장하기

#include<stdio.h>
#include<stdlib.h>

// 정수 배열의 길이 n 입력 받기
int n;
scanf("%d", &n);
// n 길이의 정수 배열 포인터 생성
int *p = (int *)malloc(n * sizeof(int));
if (p == NULL) return -1;
// 정수 저장
for (int i = 0; i<n; i++){
	scanf("%d", &p[i]);
}



3) 동적 메모리 할당을 사용한 문자열 처리 프로그램
(1) 문자열을 입력받기 위한 충분한 크기의 문자 배열 tmp 선언 (정적할당)
(2) tmp에 문자열 입력받아 저장
(3) tmp의 길이를 계산하여 그 크기+1 사이즈의 메모리 할당
(4) 할당받은 메모리 공간에 tmp 문자열 복사

>> 문자열 입력받을 메모리 할당 및 저장

#include<stdio.h>
#include<string.h>
#inlcude<stdlib.h>

char tmp[100]; // 길이가 넉넉한 문자 배열 선언
scanf("%s", tmp); // 입력 받기

char *str = (char *)malloc(strlen(tmp)); // 문자를 저장할 포인터 str 생성 및 메모리 할당
if (str == NULL) return -1; // 메모리 할당 검사
strcpy(str, tmp); // 문자열 복사



4) 동적 메모리 할당을 사용하여 2차원 배열 할당하는 프로그램
이중 포인터 사용하기

>> 문자열의 배열을 입력받을 메모리 할당 및 저장

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int n;
scanf("%d", &n); // 문자열 배열의 길이 n

// 이중 포인터 선언 및 n 만큼 메모리 할당
char **pch = (char **)malloc(n * sizeof(char *));
if (pch == NULL) return -1; // 메모리 할당 검사

// 임시로 입력받을 문자열 tmp 선언
char tmp[100];
for (int i = 0; i<n; i++){
	scanf("%s", tmp); // tmp에 문자열 입력받기
	pch[i] = (char *)malloc(strlen(tmp) + 1); // tmp 길이 +1 만큼 메모리 할당하기
    if (pch[i] == NULL) return -1; // 메모리 할당 검사
	strcpy(pch[i], tmp); // tmp에 저장된 문자열 복사
}



4. 기타 동적 메모리 할당 함수

1) calloc() 함수
- 해당 개수의 자료형 만큼 메모리 할당 및 0으로 초기화
(1) 함수 원형
- void *calloc(unsigned int num, unsigned int size);

(2) 함수 인자
num: 동적으로 할당받을 원소의 개수
size: 원소 한개의 크기(바이트 단위)

(3) 반환 값
(num*size) 바이트 수 만큼 할당하고, 할당된 메모리를 모두 0으로 초기화 후 시작 위치 반환
실패하면 NULL 반환

(4) 사용 예시

int *p = NULL;
p = (int *)calloc(5, sizeof(int));
// malloc(5*sizeof(int)); 와 유사


2) realloc() 함수
- 해당 위치의 주소에 새로운 크기의 메모리 할당
(1) 함수 원형
- void *realloc(void *ptr, unsigned int size);

(2) 함수 인자
ptr: 확장 크기를 변경할 메모리의 시작 주소
new_size: 변경 후 메모리의 전체 크기(바이트 단위)

(3) 반환 값
- ptr이 가리키는 메모리 크기를 new_size 바이트 크기로 조정하고, 조정된 메모리의 시작위치 반환
실패하면 NULL 반환

(4) 사용 예시

char *p = (char *)malloc(5);
p = (char *)realloc(p, 10);
// 5 바이트 였던 p의 메모리 크기를 10으로 변경
// 기존 위치에 새로운 크기를 확보할 수 없으면, 기존위치의 공간을 해제하고 새로운 위치에 공간 할당






728x90

+ Recent posts