[C언어] #9 동적메모리사용 (malloc, free, memset)

Ilhoon·2022년 2월 20일
2
post-thumbnail

동적메모리

정적 메모리를 우선적으로 사용하는 것이 무조건 좋다 안될 때만 동적 메모리 사용하자

  • 실수할 여지가 많고 속도가 느리기 때문에

동적 메모리의 소유주

  • 메모리를 생성한 함수에서 메모리를 해제하자

    • 함수 중간에 실수로 return해버리면 ..?
    • 함수 내에서 동적메모리를 할당 하지않고 밖에서 할당한 메모리를 매개변수로 사용하는 것이 좋다.
    • 어쩔 수 없이 함수 내에서 사용해야 한다면? 함수이름이나 변수명에 표기하자
  • 소유주가 아니라면 메모리를 해제하지말 것


메모리할당

힙 메모리 관리자에게 xxx 바이트만큼 달라고 요청

관리자는 연속된 메모리 찾아서 반환 (메모리주소형태 : 포인터로 저장가능)

malloc()

void* malloc(size_t size);

size 바이트 만큼의 메모리를 반환해줌

초기화 안해준다 (처음 들어있는 값은 쓰레기값)

메모리가 없거나 실패하면 NULL반환

반드시 free()와 함께 사용!!!

#include <stdlib.h>

size_t i;
int* nums = malloc(10 * sizeof(int));		// 10 * 4 : 40byte할당 해주세요
for (i = 0; i < 10; i++){
    nums[i] = i * 10;
}

free(nums)									// 다 사용했으니 해제해주세요

realloc()

void* realloc(void* ptr, size_t new_size)

이미 존재하는 메모리의 크기를 new_size크기로 변경

실패시 NULL을 반환하지만 기존 메모리는 해제되지 않음

  • 기존 주소를 잃어버려서 메모리 누수가 생길 수 있다.

    void* nums;
    nums = malloc(size);
    nums = realloc(nums, 2 * size);	// 실패하면 nums의 원래주소를 알 수가 없다..
  • 올바른 재할당 방법은 임시변수를 사용해서 주소 받아와서 저장

크기가 커지는 경우 (크기가 작아지는 경우도 비슷)

  • 지금 소유하는 메모리공간 뒤에 충분한 공간이 없는경우
    • 기존데이터를 복사하고 새 주소를 반환 (기존 메모리는 해제)
  • 지금 소유하는 메모리공간 뒤에 충분한 공간이 있는 경우
    • 기존 주소를 반환 (그러나 이런 경우도 반드시 기존주소를 준다는 보장은 없다.)



메모리사용

memset()

void* memset(void* dest, int ch, size_t count);
  • dest 위치부터 count 바이트만큼 ch 값으로 초기화 해주라

  • <string.h>에 있음

  • char로 초기화 됨 (1바이트씩)

    • 다른 자료형으로 초기화 하려면 for문으로 직접계산해서 해줘야한다.
    // char형으로 잘못 초기화 된 예
    void* nums;
    
    nums = malloc(4 * sizeof(int));
    memset(nums, 1000, 4 * sizeof(int));		// int 1000은 16진수로 = 00 00 03 E8 -> 실제로 E8만 저장
    free(nums);
    nums = NULL;
    
    // int형으로 직접 초기화
    p = nums;
    for (i = 0; i<4; i++){
        *p++ = 1000;
    }
    free(nums);
    nums = NULL;
  • count가 dest의 영역을 넘어설 경우, dest가 널 포인터일 경우 문제가 발생할 수 있다.


memcpy()

void* memcpy(void* dest, const void* src, size_t count);
  • <string.h>에 있음
  • src에 데이터를 count바이트 크기만큼 dest에 복사
  • count가 dest의 영역을 넘어설 경우, dest가 널 포인터일 경우 문제가 발생할 수 있다.

memcmp()

int memcmp(const void* lhs, const void* rhs, size_t count);
  • 첫 count 바이트 만큼의 메모리를 비교하는 함수
    • 같으면 0, 오른쪽이 크면 -1, 왼쪽이 크면 1
  • strcmp()와 매우 비슷
  • 널 문자를 만나도 계속 진행
  • 구조체 두 개를 비교할 때 유용
  • 비교할 데이터 이외에 다른 공간에 쓰레기 값들이 들어있는 경우 그 값이 다르다면 의도치않게 비교 결과가 나올 수 있다.
  • 포인터 변수가 있는 경우 값이 같아도 주소값이 다르면 결과가 다르게 나온다.



메모리해제

free()

void free(void* ptr)

할당받은 메모리를 해제하는 함수 (메모리 할당 함수를 통해 받은 메모리만 해제 가능)

함수에서 메모리 할당 후 까먹고 해제안했는데 함수가 반환된 상태라면...?

  • 다시 해제시켜줄 방법이 없다.

문제가 될 수 있는 경우

int* nums;

nums = malloc(LENGTH * sizeof(int));

for (i = 0; i < LENGTH; i++){
    *nums++ = 10 * (i + 1);			// nums가 최초로 할당받은 주소가 아닌 다른 주소를 가리킴
}

free(nums);							// 다른 주소를 해제해버림..

실수를 줄이는 방법

  • 해제한 메모리를 누군가 다시 사용하면?

    • free()호출 이후 널포인터를 대입해준다.

      free(nums);
      nums = NULL;
      • 확실히 해제된 메모리라는 것을 표시
      • 누군가 실수로 사용해도 안전하다

free는 몇 바이트를 해제할지 어떻게 알지?

  • malloc()함수를 호출하면 실제 할당하는 메모리 보다 조금 더 큰 메모리 할당 후 앞에 크기 데이터 채워놓음
  • 크기 데이터 이후 주소값을 사용자에게 반환
  • 그 주소를 해제할 때 앞에 어떤 데이터를 free()함수가 보고 그 크기 만큼 메모리 해제



동적메모리 사용의 베스트 프랙티스

  1. 할당 후 해제를 반드시 함께 사용하자
  2. 할당받은 변수와 연산을 위한 변수를 따로 사용하자
  3. 메모리 해제 후 NULL로 초기화 해주자
  4. 함수 내에서 동적 메모리 할당을 최소화 하자
  5. 함수 내에서 해야 할 경우 변수와 함수 이름으로 그 사실을 알려주자.
profile
꾸준하게!

0개의 댓글