F.R.I.D.A.Y.

calloc의 인자는 왜 두 개가 필요할까? 본문

DEV/C C++

calloc의 인자는 왜 두 개가 필요할까?

F.R.I.D.A.Y. 2019. 12. 5. 22:47
반응형
 

malloc VS calloc

동적할당은 프로그램에 조금만 규모가 생기면 잘 사용하게 되는 기술로 C언어에서는 함수를 이용해 동적할당을 받는데요, 동적할당을 하는 함수로는 대표적으로 아래와 같은 것들이 있습니다. malloc calloc real..

pang2h.tistory.com

 위 포스트에 댓글로 들어온 질문입니다. malloc 함수를 사용할 때와 달리 왜 calloc 함수는 두 개의 인자를 받아야 할까요?


calloc()

 이전 포스트에서도 말한 것과 같이 calloc 함수는 malloc 함수로 받은 메모리 공간을 0으로 초기화하여 메모리 시작 주소를 반환하는 함수입니다. 인자 두 개가 필요한 것은 메모리 공간을 0으로 초기화할 때 필요합니다.

 다음 코드를 보겠습니다.

 

#include <stdlib.h>

int main(void){
    char *ci;
    int  *pi;
    
    ci = malloc(sizeof(char) * 100);
    pi = malloc(sizeof(int)  *  25);
    
    for(int i = 0 ; i < 100; ++i){
        ci[i] = 0;
    }
    
    for(int i = 0 ; i < 25; ++i){
        pi[i] = 0;
    }
    
    .
    .
    .
    free(ci); free(pi);
    return 0;
}

 int 타입의 공간 25개, char 타입의 공간 100개를 할당받아 그 공간을 0으로 초기화하는 코드입니다.

 여기서 질문입니다. ci와 pi 변수가 가리키는 메모리 블록은 각각 몇 byte일까요? 두 변수 모두 100byte만큼의 메모리 시작 주소를 가지고 있습니다.

 그런데, 이 100byte의 공간을 초기화한다고 하였을 때, 어떤 블록이 더 빨리 초기화가 이루어질까요?

    for(int i = 0 ; i < 100; ++i){ // char 공간 초기화
        ci[i] = 0;
    }
    
    for(int i = 0 ; i < 25; ++i){ //   int 공간 초기화
        pi[i] = 0;
    }

 반복문의 반복 부분을 보면 char 타입으로 할당받은 공간은 100번, int 타입으로 할당받은 공간은 25번으로 반복 횟수가 4배나 차이 납니다. 여기서 우리는 예상할 수 있습니다.

 calloc 함수의 두 번째 인자가 초기화 횟수에 영향을 준다라고 말입니다.

 

 실제로 그러한지 확인해봅니다.


GCC 코드 살펴보기

 gcc 컴파일러의 calloc() 코드를 찾아봅니다.

PTR
calloc (size_t nelem, size_t elsize)
{
  register PTR ptr;  

  if (nelem == 0 || elsize == 0)
    nelem = elsize = 1;
  
  ptr = malloc (nelem * elsize);
  if (ptr) bzero (ptr, nelem * elsize); // check!
  
  return ptr;
}
// https://github.com/gcc-mirror/gcc/blob/master/libiberty/calloc.c

 보면 <check!> 라인에서 bzero 함수를 호출하는 것을 알 수 있습니다. 그럼 bzero 함수를 확인해봅니다.

void
bzero (void *to, size_t count)
{
  memset (to, 0, count);
}

 memset 함수를 사용하는 것을 알 수 있습니다. memset 함수의 경우에는 어떠한 값이 두 번째 인자에 1바이트의 값만 받을 수  있으므로 calloc 함수에서 두 번째 인자로 값을 몇을 넘기던 메모리의 크기만큼 반복을 함을 알 수 있습니다.

// 참고
PTR
memset (PTR dest, register int val, register size_t len)
{
  register unsigned char *ptr = (unsigned char*)dest;
  while (len-- > 0)
    *ptr++ = val;
  return dest;
}

 

 이런! 예상했던 것과는 달리 calloc 함수에서 두 번째 인자는 굳이 필요하지 않았습니다. 그럼 calloc 함수를 만들었던 개발자는 왜 이렇게 두 번째 인자를 추가로 작성했을까요? stackoverflow에서도 찾아보고 많은 곳에서 찾아보아도 특정한 이유로 두 개의 인자를 갖게 되었다는 이야기는 하지 않고 있습니다. 단지

  • 역사적으로 그래 왔으니까.
  • 인자 두 개로 작성하는 것이 더 명확하니까.

 등을 추측할 뿐입니다. 실제로도 malloc 함수가 사용한 하나의 인자 방식보다는 calloc 함수가 사용하는 두 개의 인자 방식이 각 요소의 크기와 그 요소의 개수를 파악하는데 더 쉽게 느껴지는 것이 사실입니다.

 

 그럼 현재에는 calloc 함수의 인자를 하나로 변경하면 안 되냐는 질문이 있을 수 있습니다. 그러나, 이러한 구조를 변경하는 것은 IT 생태계에 큰 걸림돌이 될 수 있습니다. 구조를 변경하기 전 calloc 함수를 사용하던 프로그램들은 함수 구조의 변경으로 인해 불필요한 작업을 거쳐야 하니 말입니다. 이전 세대의 프로그램과의 호환성을 위해 이제는 더 이상 사용하지 않을 함수, 구조들도 유지하는 프로그램이 많이 있습니다.

728x90
반응형

'DEV > C C++' 카테고리의 다른 글

비트 연산자 : 종류  (0) 2019.12.20
void 포인터(메모리)  (0) 2019.12.08
매크로 함수를 사용할 때 주의할 점  (1) 2019.11.26
함수 포인터를 배워야 하는 이유2 : callback 함수  (3) 2019.11.17
주가 스팬 계산하기  (1) 2019.11.14
Comments