[자료구조 - C언어] 자료구조 제1강: 동적메모리할당

2022. 4. 18. 18:52CS/자료구조

728x90

//공부 기록용 포스팅입니다. 틀린 부분이 있을 경우 댓글로 알려주시면 감사합니다! 😎

 

1. 동적 메모리 할당

  • 일반적으로 데이터를 저장할 메모리 공간을 확보하는 방법
    • 데이터를 저장할 변수를 선언하고 그 변수에 저장
  • 그렇다면! 동적메모리할당(dynamic memeory allocation)은?
    • 직접적으로 메모리를 요청해서 할당받아 데이터를 저장해 두는 것
      • 어떻게 직접적으로 메모리를 요청할 수 있어?
        • malloc 함수를 사용해서!: memory allocation
          • malloc 함수를 호출 → 동적 메모리 할당을 요청 → 요구하는 크기의 메모리를 할당 → malloc 함수가 메모리의 시작주소를 반환

 

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에 저장
int *p;
  • malloc의 입장을 생각해보면 사용자가 40byte의 메모리 공간을 요청했을 때 이 메모리의 공간을 사용자가 할당받아서 어떤 용도로 사용할 지에 대해서 malloc 함수는 알 수 없다.
    • 사용자의 마음이니까! 사용자가 정수를 저장할지, 실수를 저장할지 알 수 없으니까 → malloc 함수가 반환하는 주소는 타입이 없는 주소(void *)이다.
      • 목적이 확실하다면 if 정수) 정수들을 저장하기 위해서 (int *)으로 변환.
      p = (int *)malloc(40);
      
      • character을 저장할 거라면 → (char *)으로 변환, 반드시 필요한 것은 아님
      p = (char *)malloc(10);
      

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에 저장을 하는 순간 메모리 공간을 정수형 배열인 것처럼 사용할 수 있다.
      • 포인터와 배열은 긴밀하게 연결되어 있기 때문에

 

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언어에서 자동으로 처리되지 않는다.
        • 할당 받았지만 사용하지 않는 공간으로 계속 남아있다.
          • 당장의 오류는 없지만, 프로그램이 필요 이상의 메모리를 사용하고 누적 시 성능 문제를 야기할 수 있다.

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을 할 수 있다.

 

 

 

 


부경대학교 권오흠 교수님의 [c로 배우는 자료구조 및 여러 가지 예제 실습] 강의 정리입니다. 감사합니다.

https://www.youtube.com/watch?v=-XbHQQ8pUQY&feature=emb_title 

 

728x90