728x90
  • 헤더파일로 <stdlib.h><malloc.h> 를 포함시켜야한다.
  • 메모리 할당 함수 : malloc
  • 메모리 할당 및 초기화 : calloc
  • 메모리 추가 할당 : realloc
  • 메모리 해제 함수 : free

 

  • 동적할당: 프로그램 실행 중에 동적으로 메모리를 할당하는 것

  • 동적으로 메모리를 할당할 때는 ‘Heap’영역에 할당한다.
    • 정적 메모리 할당 방법(static) : 데이터를 저장할 때 변수를 선언하고 사용하는 방식
    • 프로그램이 시작하기 전에 사용할 메모리공간을 정해놓고 시작

 

  • 정적 메모리 할당의 문제점
    • 메모리의 크기에 따라 메모리가 낭비되거나 필요에 따른 메모리 공간이 부족해 프로그램을 다시 짜야하는 경우가 발생

 

  • void* malloc(size_t size);
    • malloc이 void* 를 리턴하는 이유
      • 동적으로 메모리를 할당할 때 정수를 저장할 지, 배열을 저장할 지, 구조체를 저장할 지 실질적으로 모르기 때문에 무한한 자료형에 맞춰진 malloc함수를 모두 만들어 놓을 수가 없다.
    • 메모리 할당에 실패하면 null 을 반환한다.

int* pi = (int* p) malloc(sizeof(int) * 10);
char* pc = (char*) malloc(sizeof(char) * 1));

typedef struct book
{
    char name[10];
    int isbn;
    int price;
    int year;
    struct book* pb;
}BOOK;

BOOK* p = (BOOK*) malloc (sizeof(BOOK) * 30;
// BOOK 구조체 30개를 저장할 수 있는 메모리 공간 할당

 

  • malloc param : 메모리 공간
  • return type : 좌측의 포인터 타입과 malloc 함수가 리턴하는 포인터의 타입은 동일해야한다.

 

  • void* calloc(size_t num, size_t size);
    • 메모리를 모두 0으로 초기화 한다.
    • 만약 성공적으로 메모리가 할당된다면, 메모리 블럭을 맨 앞 주소를 반환한다.

 

  • malloc 🆚 calloc
    • malloc은 할당된 메모리를 초기화해주지 않기 떄문에 dummy값이 들어있다.
    • calloc은 할당된 메모리를 모두 0으로 초기화한다.
    • ex_malloc_and_calloc.c

 

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


int main(void)
{
    
    int *p1 = malloc(4*sizeof(int));  // allocates enough for an array of 4 int
    int *p2 = malloc(sizeof(int[4])); // same, naming the type directly
    int *p3 = malloc(4*sizeof *p3);   // same, without repeating the type name
 
    if(p1) {
        for(int n=0; n<4; ++n) // populate the array
            p1[n] = n*n;
        for(int n=0; n<4; ++n) // print it back out
            printf("p1[%d] == %d\n", n, p1[n]);
    }
 
    
    
    int *p4 = calloc(4, sizeof(int));    // allocate and zero out an array of 4 int
    int *p5 = calloc(1, sizeof(int[4])); // same, naming the array type directly
    int *p6 = calloc(4, sizeof *p3);     // same, without repeating the type name
 
    
    if(p2) {
        for(int n=0; n<4; ++n) // print the array
            printf("p2[%d] == %d\n", n, p2[n]);
    }
 
    free(p1);
    free(p2);
    free(p3);
    free(p4);
    free(p5);
    free(p6);
}

 

  • output
>> p1[0] == 0
>> p1[1] == 1
>> p1[2] == 4
>> p1[3] == 9
>> p2[0] == 0
>> p2[1] == 0
>> p2[2] == 0
>> p2[3] == 0
Program ended with exit code: 0

 

  • void* realloc(void* ptr, size_t new_size);
    • 이미 할당받은 메모리에 추가로 메모리를 할당한다.
    • 이전 메모리 주소는 사라진다.

 

  • ex_realloc.c
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void print_storage_info(const int* next, const int* prev, int ints) {
    if (next) {
        printf("%s location: %p. Size: %d ints (%ld bytes).\n",
               (next != prev ? "New" : "Old"), (void*)next, ints, ints * sizeof(int));
    } else {
        printf("Allocation failed.\n");
    }
}
 
int main(void)
{
    const int pattern[] = {1, 2, 3, 4, 5, 6, 7, 8};
    const int pattern_size = sizeof pattern / sizeof(int);
    int *next = NULL, *prev = NULL;
 
    if ((next = (int*)malloc(pattern_size * sizeof *next))) { // allocates an array
        memcpy(next, pattern, sizeof pattern); // fills the array
        print_storage_info(next, prev, pattern_size);
    } else {
        return EXIT_FAILURE;
    }
 
    // Reallocate in cycle using the following values as a new storage size.
    const int realloc_size[] = {10, 12, 512, 32768, 65536, 32768};
 
    for (int i = 0; i != sizeof realloc_size / sizeof(int); ++i) {
        if ((next = (int*)realloc(prev = next, realloc_size[i] * sizeof(int)))) {
            print_storage_info(next, prev, realloc_size[i]);
            assert(!memcmp(next, pattern, sizeof pattern));  // is pattern held
        } else {  // if realloc failed, the original pointer needs to be freed
            free(prev);
            return EXIT_FAILURE;
        }
    }
 
    free(next); // finally, frees the storage
    return EXIT_SUCCESS;
}

 

  • output
>>> New location: 0x600000202e80. Size: 8 ints (32 bytes).
>>> New location: 0x600000c00990. Size: 10 ints (40 bytes).
>>> Old location: 0x600000c00990. Size: 12 ints (48 bytes).
>>> New location: 0x10080b400. Size: 512 ints (2048 bytes).
>>> New location: 0x108008000. Size: 32768 ints (131072 bytes).
>>> Old location: 0x108008000. Size: 65536 ints (262144 bytes).
>>> Old location: 0x108008000. Size: 32768 ints (131072 bytes).
Program ended with exit code: 0

 

💡 EXIT_SUCCESS , EXIT_FAILURE

: EXIT_SUCCESSEXIT_FAILURE 상수는 및 _exit 함수에 exit 대한 인수이며 및 함수의 atexit_onexit 반환 값

 

  • thread-safe
    • 멀티 스레드 프로그래밍 환경에서 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없음을 뜻한다.

 

  • void free(void* ptr);
    • 할당한 메모리를 해제한다.
    • 할당한 메모리를 제대로 해제해주지 않으면, 메모리 누수가 발생할 수 있다.

 

💡 메모리 누수 잡는 방법

  • gcc -g3 -fsanitize=address 로 컴파일
  • valgrind : 메모리 누수 검사 프로그램

 

malloc 선언 시, 타입 캐스팅

  • Bad Usage : int *sieve = (int *) malloc(sizeof(int) * length);
  • Good Usage : int *sieve = malloc(sizeof *sieve * length);
  • 타입 캐스팅은 안좋은 프로그래밍 관습의 일례이다.
  • 특히 포인터 캐스트의 경우 더더욱 안좋다.
  • 타입 캐스팅을 할 경우, int 오류를 일으킬 수 있는 잠재적인 위험이 많다.
  • void* 의 경우, 자동으로 다른 포인터 유형으로 안전하게 변환된다.
    • 암시적 형 변환이 자동으로 일어난다.
    • 명시적 형 변환은 위험하다.
  • Ref. https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc

 

 

 


🔗 Reference

728x90

'🌱 Dev Diary > 📄 TIL' 카테고리의 다른 글

RB Tree 구현  (0) 2022.12.06
RB Tree 이론  (1) 2022.11.29
C 정복하기(1) - 포인터  (0) 2022.11.28
알고리즘 이론 - 이진 트리  (0) 2022.11.16
알고리즘 이론 - 그래프 이론 기본  (0) 2022.11.16