calloc

malloc/calloc 관련 정리

2021. 5. 30. 21:09

42seoul에서 libft 과제의 동료평가를 진행하다보니, 고수분들께 평가받으며 내가 몰랐던 개념의 허점에 대해 많이 알게 되었다.

그에 대하여 정리하여 스스로 개념을 보완해보려 한다.

 

 

 

 


 

* malloc : 메모리를 할당

 

1) 프로토타입 

 

#include <stdlib.h>

void *malloc(size_t size);

 

2) 인자들

size : 메모리 블록의 크기(바이트 단위)

 

3) 리턴값

메모리 할당에 성공했을 경우 : 할당한 메모리 블록을 가르키는 포인터를 리턴

     (해당 포인터의 타입은 언제나 (void *)형이므로, 사용자가 원하는 타입으로 캐스팅 해줘야 함)

매모리 할당에 실패했을 경우 : 널 포인터를 리턴

 

 

주소값을 반환받기 떄문에, 할당된 힙 메모리 영역에 접근하려면 포인터를 사용해야한다.

사용 후 반드시 free() 해줘야한다 (메모리 해제)

 

 

주의할 점 : unix 기반이랑 리눅스 기반에서 다르게 작동

 

 

주의할 점 : malloc()이 실패할 경우, 해당 시점의 포인터에 아무 메모리도 할당되지 않고 null이 리턴된다.

 

    - 사이즈만큼의 여유 메모리 공간이 남아있는지 먼저 확인하고, 여유 공간이 없을 경우에 아무런 할당 없이 null 을 리턴한다.

    (처음에 나도 오해한 부분인데, malloc은 조금씩 메모리를 할당 시도를 거듭하다 갑자기 메모리가 부족하면 null을 리턴하는 것이 아니다! 애초부터 메모리 할당이 가능한지, 요청된 사이즈를 보고 확인부터 한다.)

 

 

 


 

 

* calloc() 의 특징

 

malloc과 마찬가지로 힙 메모리를 할당하는 함수이다.

 

1) 프로토타입 

 

#include <stdlib.h>

void *calloc(size_t nmemb, size_t size);

 

2) 인자들

nmemb : 다음 파라미터인 size의 갯수. 즉, 할당할 배열의 개수

size : 메모리 블록의 크기(바이트 단위)

 

3) 리턴값

메모리 할당에 성공했을 경우 : 할당한 메모리 블록을 가르키는 포인터를 리턴 (nmemb * size byte 만큼의 힙 메모리 할당)

     (해당 포인터의 타입은 언제나 (void *)형이므로, 사용자가 원하는 타입으로 캐스팅 해줘야 함)

매모리 할당에 실패했을 경우 : 널 포인터를 리턴

 

 

 

calloc()은 size byte 크기의 데이터 type을 nmemb개 저장할 수 있을 크기의 메모리를 할당

즉, size * nmemb 바이트의 메모리를 할당

calloc()은 heap 메모리를 할당하며, malloc()과는 달리 할당된 메모리를 0x00으로 초기화

 

주소값을 반환받기 떄문에, 할당된 힙 메모리 영역에 접근하려면 포인터를 사용해야한다.

사용 후 반드시 free() 해줘야한다 (메모리 해제)

 

 

 

참고)

만약, nmemb 또는 size가 0이면, NULL 또는 free() 시에 오류가 나지 않도록 적절한 포인터를 리턴한다.

 -> unix의 경우에는 1 만큼의 동적할당이 이루어짐

 

만약, long long (64bit)를 넘어서는 숫자에 대해서는 null을 리턴한다.

-> 초기화할 메모리 주소에 오버플로우가 일어날 경우, 반환된 포인터의 주소0x00으로 NULL이 되는 것을 유의해야 한다. 

만약 메모리 할당 후에 굳이 메모리에 대한 초기화가 필요 없다면, malloc()을 사용하고, 그렇지 않다면 calloc()을 사용하는 것이 좋다.

 

 

 

 

* 내 libft 과제 calloc() 구현의 보완점

 

1) size * count 가 0 일 때 예외처리

2) size * count 가 오버플로우 됬을 때 포인터가 null 주소를 갖도록 처리

 

 

 

 

 

 

참고 자료 출처 :

혹시나 문제가 된다면 바로 비공개 처리하겠습니다. 지적이나 댓글 환영합니다!

 

이번 포스팅에서는 malloc을 사용한 함수 2개 (calloc, strdup)를 구현해보겠다.

malloc을 통해 동적할당을 할 수 있고, 이를 통해 메모리를 더욱 유연하고 자유롭게 쓸 수 있다.

 

참고로, 내가 정의한 libft.h 헤더에는 <unistd.h>와 <stdlib.h>가 include 되어있다. 따라서 libft.h를 호출하면, 따로 정의하지 않고도 <unistd.h> 에 정의된 size_t 타입과 <stdlib.h>의 malloc/free를 사용할 수 있다.

 

 

 

 

 

(1) calloc : 0으로 초기화된 메모리를 할당

 

- 매뉴얼(영문번역) :

        이름 : calloc -- 메모리 할당

        시놉시스 :

                   #include <stdlib.h>

                   void *calloc(size_t count, size_t size);

        설명 :

                calloc() 함수는 메모리를 할당한다. 할당된 메모리는 그것이 어느 데이터 타입이든 쓸 수 있게 정렬된다.

                (이때 타입은 AltiVec-와 SSE-관련 타입들을 포함한다)

                free() 함수를 통해 이러한 메모리 할당 함수로 할당된 메모리를 해제할 수 있다.

                calloc() 함수는 size로 주어진 바이트의 메모리를 가진 count 개수의 객체들을 위한 충분한 공간을

                연속적으로 할당한다. 그리고 할당된 메모리의 포인터를 반환한다.

                할당된 메모리들은 0값을 가진 바이트들로 채워진다

        리턴값 : 성공할 경우, 할당된 메모리의 포인터를 반환한다.

                   만일 오류가 발생한다면, NULL 포인터를 반환한 후 errno를 ENOMEM으로 설정한다.

 

 


- 구현 코드 예시 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "libft.h"
 
void    *ft_calloc(size_t count, size_t size)
{
    size_t            i;
    unsigned char    *ptr;
 
    if (!(ptr = malloc(size * count)))
        return (0);
    i = 0;
    while (i < size * count)
    {
        ptr[i] = 0;
        i++;
    }
    return ((void *)ptr);
}
cs

- malloc을 통해 size * count만큼 메모리를 할당해주었다

- ptr 이 null(0)일 경우, 메모리 할당이 실패했으므로 ptr을 반환하여 null 포인터를 반환한다.

- 성공적으로 메모리를 할당한 경우, size * count 만큼의 메모리 영역에 unsigned char 형의 포인터를 통해 1바이트씩 접근한다. 그리고 매번 0을 할당하여 할당한 메모리 영역을 모두 0으로 초기화한다

- 메모리 초기화까지 성공하면 (unsigned char *) 형의 ptr을 (void *)로 형변환하여 리턴해준다. 

 

- 참고) errno를 ENOMEM으로 설정한다는 것의 의미

더보기

errno에러가 발생할 때 마다 기록을 저장하고 있는 전역변수이다.

errno 전역 변수는 프로그램이 시작할 때 "에러가 발생하지 않았다"는 뜻의 0으로 시작해서, 에러가 발생할 때마다 새로운 에러 번호로 갱신된다. (매번 갱신되는 것이 아니라 에러 발생 시 마다 갱신)

 

ENOMEM : not enough memory (kernel이 사용할 수 있는 메모리 부족)

 

- 주의) malloc() 실패 시의 널가드를 뺴먹지 말아야한다 (이걸로 동료평가 디팬스 실패했었음)

더보기

실제 라이브러리의 calloc()을 호출하여 결과를 확인한 경우, size * count 가 메모리 주소 범위를 벗어날 경우, 메모리 주소로 0이 들어가는 결과가 출력된다.

이를 유의하여 동일하게 구현할 수 있도록 주의해야한다.

 

 

 

(2) strdup : 메모리 할당 및 문자열 복사

 

- 매뉴얼(영문번역) :

        이름 : strdup -- 문자열의 사본을 저장

        라이브러리 : 표준 C 라이브러리 (libc, -lc)

        시놉시스 :

                   #include <string.h>

                   char *strdup(const char *s1);

        설명 :

                strdup() 함수는 문자열 s1의 사본을 저장하기 위한 충분한 메모리를 할당한다. 그리고 복사를 수행한다.

                그리고 그것에 대한 포인터를 반환한다. 해당 포인터는 그 후에 free()의 인자로 사용될 수 있다.

                할당할 수 있는 메모리가 불충분할 경우, NULL이 반환되며, errno가 ENOMEM으로 설정된다.

        리턴값 : 성공할 경우, 할당된 메모리의 포인터를 반환한다.

                   만일 오류가 발생한다면, NULL 포인터를 반환한 후 errno를 ENOMEM으로 설정한다.

 

 


- 구현 코드 예시 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "libft.h"
 
char            *ft_strdup(const char *s1)
{
    char    *ptr;
    size_t    len;
    size_t    i;
 
    i = 0;
    len = ft_strlen(s1);
    ptr = malloc(sizeof(char* (len + 1));
    if (!(ptr))
        return (0);
    while (i < len)
    {
        ptr[i] = s1[i];
        i++;
    }
    ptr[i] = '\0';
    return (ptr);
}
cs

- 우선 주어진 문자열의 길이 + 1 만큼 공간을 할당해 주었다.

- ptr에 동적할당이 실패한 경우 (하드웨어의 여유 메모리 부족 등 여러가지 원인), null을 반환한다.

- 동적할당 된 포인터에 주어진 문자열을 복사한 후, 끝에 null을 넣고 해당 포인터를 리턴한다.

- 문자열의 길이값을 size_t 형의 변수 len으로 받았기 때문에, while 문의 조건식에서 len과 비교해야하는 인덱스 변수 i 또한 size_t로 선언해주었다.

 

 

 

 

 

 

참고 자료 출처 :

+ Recent posts