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으로 변경
// 기존 위치에 새로운 크기를 확보할 수 없으면, 기존위치의 공간을 해제하고 새로운 위치에 공간 할당
'프로그래밍 언어 개념정리 > C언어 개념 정리' 카테고리의 다른 글
C언어 개념 정리: 파일 입출력 (0) | 2024.12.09 |
---|---|
C언어 개념 정리: 연산자/함수/자료형 심화 (0) | 2024.11.21 |
C언어 개념 정리: 구조체 (0) | 2024.11.15 |
C언어 개념 정리: 문자열 (1) | 2024.11.13 |
C언어 개념 정리: 포인터 (0) | 2024.11.12 |