_countof 매크로는 배열의 원소 개수를 돌려주는 서비스 매크로이다.
StringCchCopystrcpy_s 같은 함수들을 사용할 때 편리하게 사용할 수 있다.

_countof 매크로는 다음과 같이 생겼다.

#if !defined(_countof)
  #if !defined(__cplusplus)
    #define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
  #else
    extern "C++"
    {
      template <typename T, size_t N>
      char (*__countof_helper(UNALIGNED T (&_Array)[N]))[N];
      #define _countof(_Array) (sizeof(*__countof_helper(_Array)) + 0)
    }
  #endif
#endif

C언어를 사용할 때와 C++를 사용할 때의 구현이 다르게 되어있다.
C언어에서 사용되는 방식은 대부분의 사람들이 잘 알고 있는 방식일 것이며, C++에서는 템플릿을 이용해서 배열의 개수를 구하고 있다.

여기서 궁금한 점은

  1. 템플릿을 통해 어떻게 배열의 원소의 개수를 구할 수 있는가.
  2. 도대체 왜 C와 C++을 전처리기를 사용해서까지 따로 구현했을까. 그냥 C 구현 하나만 쓰지.

1번 질문의 답은 아래 블로그에 잘 설명이 되어있다.
C++ 템플릿으로 배열의 원소 개수를 구하는 방법

2번 질문의 답은 C 구현 방식에 약간의 문제가 있기 때문이다.
배열이 아니라 포인터일 경우에 C 방식은 제대로 개수를 구해주지 못한다.
그럼 템플릿 방식은 제대로 구해주냐 하면 물론 그럴수는 없다.
그래도 컴파일 에러를 내주기 때문에 좀 더 낫다. C 방식은 컴파일이 잘 되어버리고 잘못된 결과를 돌려준다.

아래 코드를 한번 보자. 설명을 쉽게 하기 위해 매크로 이름을 바꿨다.

#define _countof_c(_Array) (sizeof(_Array) / sizeof(_Array[0]))

template <typename T, size_t N>
char (*__countof_helper(T (&_Array)[N]))[N];
#define _countof_cpp(_Array) (sizeof(*__countof_helper(_Array)) + 0)

void c_version(int* p) // int ar[] 과 같은 형식으로 넘어올 때도 마찬가지이다.
{
  printf("%d\n", _countof_c(p));
}

void cpp_version(int* p)
{
  printf("%d\n", _countof_cpp(p));
}

int _tmain(int argc, _TCHAR* argv[])
{
  int a[100];
  printf("%d\n", _countof_c(a)); // 100 올바른 결과
  printf("%d\n", _countof_cpp(a)); // 100 올바른 결과

  c_version(a); // 1 틀린 결과를 내어줘버렸다. 다행히 경고(C6384)는 발생한다.
  cpp_version(a); // cpp_version함수는 컴파일이 안된다.

  return 0;
}

C 방식 매크로를 쓸 때 언제 문제가 되는지를 알고 있어야 한다.
C++로 코딩하고 있음에도 불구하고 해당 매크로를 C방식으로 직접 만들어서 사용하는 경우를 보았다.
그냥 _countof를 사용하자.

함께 읽으면 좋은 글: