[자료구조 - C언어] 자료구조 제1강: 동적메모리할당
2022. 4. 18. 18:52ㆍCS/자료구조
728x90
//공부 기록용 포스팅입니다. 틀린 부분이 있을 경우 댓글로 알려주시면 감사합니다! 😎
1. 동적 메모리 할당
- 일반적으로 데이터를 저장할 메모리 공간을 확보하는 방법
- 데이터를 저장할 변수를 선언하고 그 변수에 저장
- 그렇다면! 동적메모리할당(dynamic memeory allocation)은?
- 직접적으로 메모리를 요청해서 할당받아 데이터를 저장해 두는 것
- 어떻게 직접적으로 메모리를 요청할 수 있어?
- malloc 함수를 사용해서!: memory allocation
- malloc 함수를 호출 → 동적 메모리 할당을 요청 → 요구하는 크기의 메모리를 할당 → malloc 함수가 메모리의 시작주소를 반환
- malloc 함수를 사용해서!: memory allocation
- 어떻게 직접적으로 메모리를 요청할 수 있어?
- 직접적으로 메모리를 요청해서 할당받아 데이터를 저장해 두는 것
2. malloc 함수
- malloc 함수를 사용하기 위해서는 → #include <stdlib.h> 헤더 필요
- 변수를 선언하지 않고 10개의 정수를 저장할 메모리 공간이 필요하다 → malloc으로 메모리 공간을 동적으로 할당할 수 있다.
#include <stdio.h>
#include <stdlib.h>
int main(void){
int *p;
p = (int*) malloc(40);
if (p == NULL){
/*동적 메모리 할당이 실패*/
/* 적절한 조취를 취한다.*/
}
p[0] = 12;
p[1] = 24;
*(p+2) = 36;
}
2-1. 메모리 동적 할당
p = (int*)malloc(40);
- 할당받을 메모리의 크기를 byte단위로 지정한다. (10개의 정수를 저장하기 위한 40byte 요청)
- malloc 함수는 메모리 어딘가에 연속된 40byte의 메모리 공간을 확보한 다음 그 메모리 공간의 시작주소(첫 번째 byte)의 주소를 리턴
- 리턴 주소를 잊어버리면 할당받은 메모리를 사용할 방법이 없다. → 리턴 주소를 반드시 보관: (메모리 주소를 저장해두는 변수인) 포인터 변수 p에 저장
- malloc 함수는 메모리 어딘가에 연속된 40byte의 메모리 공간을 확보한 다음 그 메모리 공간의 시작주소(첫 번째 byte)의 주소를 리턴
int *p;
- malloc의 입장을 생각해보면 사용자가 40byte의 메모리 공간을 요청했을 때 이 메모리의 공간을 사용자가 할당받아서 어떤 용도로 사용할 지에 대해서 malloc 함수는 알 수 없다.
- 사용자의 마음이니까! 사용자가 정수를 저장할지, 실수를 저장할지 알 수 없으니까 → malloc 함수가 반환하는 주소는 타입이 없는 주소(void *)이다.
- 목적이 확실하다면 if 정수) 정수들을 저장하기 위해서 (int *)으로 변환.
p = (int *)malloc(40);
- character을 저장할 거라면 → (char *)으로 변환, 반드시 필요한 것은 아님
p = (char *)malloc(10);
- 사용자의 마음이니까! 사용자가 정수를 저장할지, 실수를 저장할지 알 수 없으니까 → malloc 함수가 반환하는 주소는 타입이 없는 주소(void *)이다.
2-2. 예외처리
if (p == NULL){
/*동적 메모리 할당이 실패*/
/* 적절한 조취를 취한다.*/
}
- 일반적으로 malloc 함수를 사용할 때 예외처리를 해두면 좋다.
- malloc 함수를 실행했는데 p가 null이라면 어떤 이유로 malloc이 실패했다. → 매우 예외적인 상황!
2-3. malloc 할당 메모리 사용
p[0] = 12;
p[1] = 24;
*(p+2) = 36;
- 예외처리를 통과하면 정상적으로 메모리를 할당받은 것
- malloc으로 할당받은 메모리는 보통의 배열처럼 사용한다.
- 40byte의 메모리 공간을 할당받아서 정수형 포인터 p에 저장을 하는 순간 메모리 공간을 정수형 배열인 것처럼 사용할 수 있다.
- 포인터와 배열은 긴밀하게 연결되어 있기 때문에
- 40byte의 메모리 공간을 할당받아서 정수형 포인터 p에 저장을 하는 순간 메모리 공간을 정수형 배열인 것처럼 사용할 수 있다.
3. 배열 키우기(array reallocation)
- C언어에서 배열을 선언할 때 크기를 지정하지 않고 배열을 선언할 수는 없다. → 따라서 배열의 크기를 충분히 크게 선언하는 수밖에 없다.
- 하지만! 동적으로 할당된 배열은 공간이 부족할 경우 더 큰 배열을 할당하여 사용할 수 있다.
int * array = (int *)malloc(4*sizeof(int));
array[0] = 1; array[1] = 2; *(array+2) = 3;
//array[4] = 4; 공간부족
int *tmp = (int *)malloc(8*sizeof(int));
int i;
for (i=0; i<4; i++)
tmp[i] = array[i];
array = tmp;
array[4] = 4; array[5] = 5;
3-1. sizeof(type)
int * array = (int *)malloc(4*sizeof(int));
// 1. int *array = (int *)malloc(16);
// 2. int array[4];
- 1번처럼 크기가 4인 정수 배열을 할당한 것과 같다.
- 하나의 정수는 ‘보통' 4byte로 표현하는데 ‘반드시' 그런 것은 아니다.
- 시스템, 플랫폼에 따라 다르기 때문에 sizeof(타입)을 사용하는 것이 좋다 → 코드의 호환성 보장
- 2번은 3-5에서 서술.
3-2. array의 크기가 부족한 상황 발생
- 처음에 할당받은 16byte의 연속된 공간의 ‘바로 그 다음'공간은 이미 다른 용도로 사용될 수 있기 때문에
- 할당 받은 배열의 크기 자체를 그냥 키우는 것은 가능하지 않다.
- 어딘가 다른 메모리의 다른 곳에 더 큰 배열을 할당 받은 다음
- 원래 배열의 데이터를 새로 할당 받은 더 큰 배열로 옮기고 새로 할당 받은 공간을 사용
- 어딘가 다른 메모리의 다른 곳에 더 큰 배열을 할당 받은 다음
- 할당 받은 배열의 크기 자체를 그냥 키우는 것은 가능하지 않다.
int *tmp = (int *)malloc(8*sizeof(int));
int i;
for (i=0; i<4; i++)
tmp[i] = array[i];
- 원래 할당받은 16byte의 배열의 크기가 부족 → 크기가 두배인 32byte 배열을 임시적으로 tmp라는 포인터 변수에 동적 메모리 할당 받음
- array의 데이터를 tmp로 옮김 = 원래 배열에 저장되어 있던 모든 값을 새로 할당받은 데이터로 copy
array = tmp;
- tpm는 32byte의 메모리 공간의 시작 주소를 가지고 있다. → array에 tmp를 저장하면 array가 새로 할당받은 메모리 공간의 주소를 갖게 된다.
- array: 포인터 변수, 처음 할당받은 16byte의 시작 주소를 저장
- tmp: 포인터 변수, 새로 할당 받은 32byte의 시작 주소를 저장
- 포인터 변수들 간의 치환문도 보통 변수들 간의 치환문과 동일하다.
array[4] = 4;
array[5] = 5;
- 새로운 공간을 사용할 수 있다.
1. 16byte의 배열의 주소를 가지고 있는 포인터 변수 array
2. 32byte의 배열의 주소를 가지고 있는 포인터 변수 tmp
3. array의 데이터를 tmp에 복사
4. array의 시작 주소를 tmp의 시작주소로 변경(= tmp의 시작 주소를 array에 저장)
=> array는 32byte의 새로운 메모리 공간의 시작 주소를 가지게 된다.
3-4. 가비지(garbage)
- 치환 후 원래 사용하던 16byte의 주소를 가진 변수는 없다.
- 어떤 프로그램이 메모리를 할당받았지만, 할당 받은 메모리의 주소를 아무도 가지고 있지 않게 되면 그 메모리 공간을 가비지라고 부른다.
- 이런 가비지는 C언어에서 자동으로 처리되지 않는다.
- 할당 받았지만 사용하지 않는 공간으로 계속 남아있다.
- 당장의 오류는 없지만, 프로그램이 필요 이상의 메모리를 사용하고 누적 시 성능 문제를 야기할 수 있다.
- 할당 받았지만 사용하지 않는 공간으로 계속 남아있다.
- 이런 가비지는 C언어에서 자동으로 처리되지 않는다.
- 어떤 프로그램이 메모리를 할당받았지만, 할당 받은 메모리의 주소를 아무도 가지고 있지 않게 되면 그 메모리 공간을 가비지라고 부른다.
3-5. int array[4]; vs int * array = (int )malloc(4sizeof(int));
//1 - array = tmp; 불가
int array[4];
//2 - array = tmp; 가능
int * array = (int *)malloc(4*sizeof(int));
array = tmp;
- array = tmp;에서 오류 발생
- int array[4]의 array는 배열의 이름, 배열의 이름은 시작 주소를 저장하는 포인터 변수
- 이때 그 값을 변경할 수 없다.(이전 포스팅 참조 https://ksk9820.tistory.com/130)
- 따라서 int array[4]; 는 시작 주소를 변경할 수 없기 때문에 사용할 수 없다.
- 일반 포인터 변수의 값은 언제든 수정 가능하다.
- 따라서 int * array = (int )malloc(4sizeof(int)); 을 사용하면 array reallocation을 할 수 있다.
- 이때 그 값을 변경할 수 없다.(이전 포스팅 참조 https://ksk9820.tistory.com/130)
- int array[4]의 array는 배열의 이름, 배열의 이름은 시작 주소를 저장하는 포인터 변수
부경대학교 권오흠 교수님의 [c로 배우는 자료구조 및 여러 가지 예제 실습] 강의 정리입니다. 감사합니다.
https://www.youtube.com/watch?v=-XbHQQ8pUQY&feature=emb_title
728x90
'CS > 자료구조' 카테고리의 다른 글
[자료구조 - C언어] 자료구조 제5강: 전화번호부 v2.0 (0) | 2022.05.06 |
---|---|
[자료구조 - C언어] 자료구조 제4강: 전화번호부 v1.0 (0) | 2022.04.28 |
[자료구조 - C언어] 자료구조 제3강: 문자열 예제 (0) | 2022.04.26 |
[자료구조 - C언어] 자료구조 제2강: 문자열 (0) | 2022.04.25 |
[자료구조 - C언어] 자료구조 제1강: C언어에서 포인터, 배열, 포인터 연산 (0) | 2022.04.17 |