C 언어 malloc

IT 위키

malloc은 C 언어 표준 라이브러리에서 동적 메모리를 할당하기 위해 사용하는 함수이다. 프로그램 실행 중 필요한 크기의 메모리를 힙 영역에 할당하고, 할당된 메모리의 시작 주소를 포인터로 반환한다.

malloc은 memory allocation의 약자로, 실행 시간에 필요한 메모리를 동적으로 확보할 때 사용한다. 배열의 크기나 자료구조의 크기를 컴파일 시점에 확정할 수 없을 때 유용하다.

malloc 함수는 stdlib.h 헤더에 선언되어 있다.

#include <stdlib.h>

malloc으로 할당한 메모리는 자동으로 해제되지 않으므로, 사용이 끝나면 반드시 free 함수를 사용하여 해제해야 한다.

함수 원형

[편집 | 원본 편집]

malloc 함수의 원형은 다음과 같다.

void *malloc(size_t size);
항목 설명
반환형 void 포인터
매개변수 할당할 메모리 크기
단위 바이트
실패 시 반환값 NULL

malloc은 요청한 크기만큼의 메모리를 바이트 단위로 할당한다. 반환형은 `void *`이므로 어떤 자료형의 포인터로도 변환하여 사용할 수 있다.

기본 사용법

[편집 | 원본 편집]

정수 1개를 저장할 메모리를 동적으로 할당하는 예는 다음과 같다.

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

int main(void) {
    int *p = malloc(sizeof(int));

    if (p == NULL) {
        return 1;
    }

    *p = 10;
    printf("%d\n", *p);

    free(p);
    return 0;
}

위 코드에서 `malloc(sizeof(int))`는 정수 1개를 저장할 수 있는 크기의 메모리를 할당한다.

배열 동적 할당

[편집 | 원본 편집]

정수 10개를 저장할 배열 공간을 동적으로 할당하는 예는 다음과 같다.

int *arr = malloc(sizeof(int) * 10);

사용 예:

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

int main(void) {
    int n = 10;
    int *arr = malloc(sizeof(int) * n);

    if (arr == NULL) {
        return 1;
    }

    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }

    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }

    free(arr);
    return 0;
}

malloc으로 할당한 메모리는 배열처럼 인덱스를 이용하여 접근할 수 있다.

메모리 초기화

[편집 | 원본 편집]

malloc으로 할당한 메모리는 초기화되지 않는다. 즉, 할당된 공간에는 이전에 사용되던 쓰레기값이 들어 있을 수 있다.

예:

int *p = malloc(sizeof(int));

이때 `*p`의 값은 정해져 있지 않다. 사용하기 전에 직접 초기화해야 한다.

*p = 0;

메모리를 0으로 초기화하면서 할당하려면 calloc을 사용할 수 있다.

malloc과 calloc

[편집 | 원본 편집]

malloc과 calloc은 모두 동적 메모리를 할당하지만 초기화 여부가 다르다.

구분 malloc calloc
목적 지정한 크기의 메모리 할당 지정한 개수와 크기의 메모리 할당
초기화 초기화하지 않음 0으로 초기화
매개변수 전체 바이트 크기 요소 개수와 요소 크기
malloc(sizeof(int) * 10) calloc(10, sizeof(int))

malloc과 realloc

[편집 | 원본 편집]

realloc은 이미 할당한 동적 메모리의 크기를 변경할 때 사용한다.

int *arr = malloc(sizeof(int) * 10);
int *tmp = realloc(arr, sizeof(int) * 20);

realloc은 기존 데이터를 가능한 한 유지하면서 메모리 크기를 변경한다. 다만 실패할 수 있으므로 임시 포인터에 결과를 받은 뒤 확인하는 방식이 안전하다.

int *tmp = realloc(arr, sizeof(int) * 20);

if (tmp != NULL) {
    arr = tmp;
}

malloc과 free

[편집 | 원본 편집]

malloc으로 할당한 메모리는 사용 후 free로 해제해야 한다.

int *p = malloc(sizeof(int));

if (p != NULL) {
    *p = 10;
    free(p);
}

free를 호출하지 않으면 메모리 누수가 발생할 수 있다.

NULL 확인

[편집 | 원본 편집]

malloc은 메모리 할당에 실패하면 NULL을 반환한다. 따라서 malloc의 반환값은 사용 전에 확인하는 것이 안전하다.

int *p = malloc(sizeof(int));

if (p == NULL) {
    printf("memory allocation failed\n");
    return 1;
}

메모리 부족 상황에서 NULL 확인 없이 포인터를 사용하면 프로그램이 비정상 종료될 수 있다.

형 변환

[편집 | 원본 편집]

C 언어에서는 malloc의 반환값을 명시적으로 형 변환하지 않아도 된다.

권장 예:

int *p = malloc(sizeof(int));

다음과 같이 명시적 형 변환을 하는 코드도 볼 수 있다.

int *p = (int *)malloc(sizeof(int));

다만 C에서는 malloc 반환값을 캐스팅하지 않는 방식이 일반적으로 권장된다. 헤더 파일 누락으로 인한 경고를 가릴 수 있기 때문이다.

sizeof 사용

[편집 | 원본 편집]

malloc을 사용할 때는 자료형 크기를 직접 숫자로 쓰기보다 sizeof를 사용하는 것이 좋다.

좋은 예:

int *arr = malloc(sizeof(int) * n);

또는 다음처럼 포인터가 가리키는 대상의 크기를 기준으로 작성할 수도 있다.

int *arr = malloc(sizeof(*arr) * n);

이 방식은 자료형이 바뀌어도 sizeof 부분을 수정하지 않아도 되어 실수를 줄일 수 있다.

구조체 동적 할당

[편집 | 원본 편집]

구조체도 malloc으로 동적 할당할 수 있다.

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

struct Student {
    int id;
    double score;
};

int main(void) {
    struct Student *s = malloc(sizeof(struct Student));

    if (s == NULL) {
        return 1;
    }

    s->id = 1;
    s->score = 95.5;

    printf("%d %.1f\n", s->id, s->score);

    free(s);
    return 0;
}

구조체 포인터를 통해 멤버에 접근할 때는 `->` 연산자를 사용한다.

문자열 동적 할당

[편집 | 원본 편집]

문자열을 저장할 메모리도 malloc으로 할당할 수 있다.

char *str = malloc(sizeof(char) * 6);

문자열은 마지막에 널 문자 `\0`이 필요하므로, 필요한 문자 수보다 1바이트 더 크게 할당해야 한다.

예를 들어 "hello"는 5글자이지만 실제로는 6바이트가 필요하다.

h e l l o \0

2차원 배열 동적 할당

[편집 | 원본 편집]

2차원 배열은 여러 방식으로 동적 할당할 수 있다.

포인터 배열을 이용하는 방식은 다음과 같다.

int **arr = malloc(sizeof(int *) * rows);

for (int i = 0; i < rows; i++) {
    arr[i] = malloc(sizeof(int) * cols);
}

해제할 때는 할당한 순서의 반대로 각 행을 먼저 해제한 뒤 포인터 배열을 해제한다.

for (int i = 0; i < rows; i++) {
    free(arr[i]);
}

free(arr);

힙 영역

[편집 | 원본 편집]

malloc으로 할당되는 메모리는 일반적으로 영역에 위치한다.

C 프로그램의 메모리 영역은 보통 다음과 같이 설명된다.

영역 설명
코드 영역 실행할 프로그램 코드 저장
데이터 영역 전역 변수와 정적 변수 저장
스택 영역 지역 변수, 함수 호출 정보 저장
힙 영역 동적 메모리 할당에 사용

힙 영역은 실행 중 필요한 크기의 메모리를 할당하고 해제할 수 있지만, 개발자가 직접 관리해야 한다.

malloc 사용 시 주의사항

[편집 | 원본 편집]
  • 할당 실패 여부를 확인한다.
    • malloc이 NULL을 반환할 수 있으므로 사용 전에 확인한다.
  • 사용 후 반드시 free한다.
    • 해제하지 않으면 메모리 누수가 발생한다.
  • 해제한 포인터를 다시 사용하지 않는다.
    • 이미 해제된 메모리를 참조하면 use-after-free 오류가 발생한다.
  • 같은 메모리를 두 번 해제하지 않는다.
    • double free 오류가 발생할 수 있다.
  • 필요한 크기를 정확히 계산한다.
    • 크기 계산이 잘못되면 버퍼 오버플로우가 발생할 수 있다.
  • 문자열에는 널 문자 공간을 포함한다.
    • 문자열 길이보다 1바이트 더 크게 할당해야 한다.
  • 해제 후 포인터를 NULL로 설정하면 안전성에 도움이 된다.
    • 실수로 다시 사용하는 위험을 줄일 수 있다.

예:

free(p);
p = NULL;

대표 오류

[편집 | 원본 편집]

메모리 누수

[편집 | 원본 편집]

할당한 메모리를 해제하지 않으면 메모리 누수가 발생한다.

void func(void) {
    int *p = malloc(sizeof(int) * 100);

    if (p == NULL) {
        return;
    }

    // free(p)가 없음
}

널 포인터 역참조

[편집 | 원본 편집]

malloc 실패 후 NULL 포인터를 그대로 사용하면 오류가 발생한다.

int *p = malloc(sizeof(int));

*p = 10;   // p가 NULL이면 오류

use-after-free

[편집 | 원본 편집]

free한 뒤 해당 포인터를 다시 사용하는 오류이다.

int *p = malloc(sizeof(int));
free(p);

*p = 10;   // 잘못된 사용

double free

[편집 | 원본 편집]

같은 메모리를 두 번 해제하는 오류이다.

int *p = malloc(sizeof(int));

free(p);
free(p);   // 잘못된 사용

버퍼 오버플로우

[편집 | 원본 편집]

할당한 크기보다 더 많이 쓰면 버퍼 오버플로우가 발생한다.

int *arr = malloc(sizeof(int) * 3);

arr[3] = 10;   // 인덱스 범위 초과

malloc 관련 함수

[편집 | 원본 편집]
함수 설명
malloc 지정한 바이트 크기의 메모리를 할당한다.
calloc 지정한 개수와 크기의 메모리를 0으로 초기화하여 할당한다.
realloc 기존 동적 메모리의 크기를 변경한다.
free 동적으로 할당한 메모리를 해제한다.

활용 분야

[편집 | 원본 편집]

malloc은 다음과 같은 경우에 사용된다.

  • 실행 중 크기가 결정되는 배열 생성
  • 연결 리스트 구현
  • 트리 구현
  • 그래프 구현
  • 동적 문자열 처리
  • 파일 크기에 따른 버퍼 생성
  • 자료구조의 노드 생성
  • 가변 크기 데이터 처리

자료구조와 malloc

[편집 | 원본 편집]

C 언어에서 연결 리스트, 트리, 그래프 같은 동적 자료구조는 malloc을 사용하여 노드를 생성하는 경우가 많다.

연결 리스트 노드 예:

struct Node {
    int data;
    struct Node *next;
};

struct Node *node = malloc(sizeof(struct Node));

동적 자료구조는 필요한 만큼 노드를 생성하고, 더 이상 필요하지 않을 때 해제할 수 있다는 장점이 있다.

malloc과 보안

[편집 | 원본 편집]

malloc 사용 오류는 보안 취약점으로 이어질 수 있다.

대표적인 취약점은 다음과 같다.

안전한 사용을 위해서는 크기 검증, NULL 확인, 경계 검사, 해제 후 포인터 관리, 정적 분석 도구 활용 등이 필요하다.

관련 개념 비교

[편집 | 원본 편집]

malloc과 정적 배열

[편집 | 원본 편집]
구분 정적 배열 malloc
크기 결정 컴파일 시점 또는 선언 시점 실행 시점
저장 영역 스택 또는 데이터 영역 힙 영역
해제 방식 자동 해제 또는 프로그램 종료 시 해제 free로 직접 해제
크기 변경 어려움 realloc으로 가능

malloc과 calloc

[편집 | 원본 편집]
구분 malloc calloc
초기화 하지 않음 0으로 초기화
매개변수 전체 바이트 수 요소 개수, 요소 크기
속도 초기화가 없어 상대적으로 빠를 수 있음 0 초기화 비용이 있음

malloc과 realloc

[편집 | 원본 편집]
구분 malloc realloc
목적 새 메모리 할당 기존 메모리 크기 변경
기존 데이터 없음 가능한 한 유지
사용 시점 처음 할당할 때 크기를 늘리거나 줄일 때

학습 관점

[편집 | 원본 편집]

C 언어에서 malloc은 포인터, 배열, 구조체, 자료구조, 메모리 관리와 연결되는 핵심 개념이다.

학습 시에는 다음 내용을 함께 이해해야 한다.

  • 포인터와 주소
  • 힙 영역과 스택 영역
  • sizeof 사용
  • NULL 반환 확인
  • free를 통한 해제
  • malloc과 calloc의 차이
  • malloc과 realloc의 차이
  • 메모리 누수
  • 버퍼 오버플로우
  • use-after-free
  • double free

시험 포인트

[편집 | 원본 편집]
  • malloc은 C 언어에서 동적 메모리를 할당하는 함수이다.
  • malloc은 stdlib.h에 선언되어 있다.
  • malloc의 매개변수는 할당할 바이트 수이다.
  • malloc은 할당된 메모리의 시작 주소를 반환한다.
  • malloc의 반환형은 void 포인터이다.
  • malloc은 실패 시 NULL을 반환한다.
  • malloc으로 할당된 메모리는 초기화되지 않는다.
  • malloc으로 할당한 메모리는 free로 해제해야 한다.
  • free하지 않으면 메모리 누수가 발생할 수 있다.
  • 해제한 메모리를 다시 사용하면 use-after-free 오류가 발생할 수 있다.
  • 같은 포인터를 두 번 free하면 double free 오류가 발생할 수 있다.
  • 문자열 공간을 malloc으로 할당할 때는 널 문자 공간까지 고려해야 한다.
  • malloc은 배열, 구조체, 연결 리스트 등 동적 자료구조 구현에 자주 사용된다.

같이 보기

[편집 | 원본 편집]