우리 회사는 코드 리뷰 결과를 주기적으로 회신하여 코드의 품질을 제고하는 활동을 진행한다. 코드 리뷰어가 점검 결과를 전달하면 구현부서에서
이를 다시 검토하여 보완 여부를 결정하는 식이다.
한 번은 아래와 같은 템플릿 함수에 대하여 매개변수 T1, T2의 범위 체크(range check)를 요청받았다.
(코드는 샘플코드로 대체한다.)
template <typename T1, typename T2>
void update(unsigned short idx, T1 arr1[], T2, arr2[])
{
if(idx == FA_1)
{
arr1[3] = arr2[3]; // arr1과 arr2 배열의 범위를 알 수 없으므로 문제가 있는 코드입니다.
}
else if(idx == FA_2)
{
arr1[2] = arr2[2];
}
else
{
arr1[0] = arr2[0];
}
}
아마도 이 코드를 작성한 개발자는 매개변수의 타입 및 배열의 크기를 일반화 하려는 목적으로 함수 템플릿으로 기능을 구현한 것으로 추정할 수 있다.C/C++ 개발자들은 함수의 매개변수로 배열을 전달할 때 포인터 변수를 사용하기도 하는데, 이는 전달 받는 함수에서는 배열의 크기를 잃게 되므로 구현시 주의해야 한다.
(이러한 현상을 타입 디케이-Type Decay-라고 한다.)
이왕 배열과 포인터 얘기가 나옴 김에 다소 원론적인 질문을 던져보기로 한다.
C++ 에서 배열과 포인터는 어떻게 다른가 ?
첫째, 포인터는 변수이고 배열 상수이다.
쉽게 말해 아래와 같은 코드는 허용하지 않는다.
int* ptr = NULL; // ptr은 Pointer type
int arr1[10] = { 0, }; // arr1, arr2는 Array type
int arr2[10] = { 0, };
ptr = arr1; // OK, Pointer는 변수
arr1 = arr2; // NOK, arr1, arr2는 상수이다.
둘째, 포인터가 가리키는 메모리의 크기는 동적으로 결정할 수 있지만, 배열이 가리키는 메모리의 크기는 선언할 때 정적으로 결정된다.
셋째, 배열을 함수의 매개변수로 전달할 때 배열 타입이 아닌 포인을 사용할 경우 배열의 크기는 전달할 수 없다. 포인터 타입으로 매개변수를 전달할 경우 배열의 크기 정보를 잃게 된다(아까 말했던 타입 디케이).
아래의 코드를 보자
void funcP(int* arr)
{
printf("sizeof arr(%zu)\n", sizeof(arr)); // 결과는 '4'아니면 '8'
}
int main(void)
{
int arr1[10] = { 0, };
printf("sizeof arr(%zu)\n", sizeof(arr1)); // 결과는 '10'
funcP(arr1);
return 0;
}
C/C++에서 배열의 정의는 다음과 같다.
http://www.cplusplus.com/doc/tutorial/arrays/
즉, 배열 변수를 선언하는 순간 해당 변수는 '타입'과 '크기'정보를 포함하게 된다.
이제 마지막으로 다시 처음의 코드로 돌아와서 코드 리뷰어의 요청사항을 어떻게 반영했는지 살펴보자.
template <typename T1, typename T2, size_t N, size_t M>
void update(unsigned short idx, T1 (&arr1)[N], T2 (&arr2)[M])
// 배열 매개변수를 레퍼런스로 전달 받았다.
// 원래의 코드에서는 포인터로 전달 받았기 때문에 크기를 알 수가 없었음.
// 이제 타입 디케이 현상 없음.
{
if(idx == FA_1)
{
if(3 >= N || 3 >= M) return; // 변수 범위를 체크합니다.
arr1[3] = arr2[3];
}
else if(idx == FA_2)
{
if(2 >= N || 2 >= M) return; // 변수 범위를 체크합니다.
arr1[2] = arr2[2];
}
else
{
if(1 >= N || 1 >= M) return; // 변수 범위를 체크합니다.
arr1[0] = arr2[0];
}
}
위 코드는 매개변수로 전달하는 배열의 크기가 다양할 경우 '코드 부풀림(Code Bloating)' 현상이 발생하지 않도록 주의해야 한다. 실제 코드에서는 한정된 크기의 배열에 한하여 해당 함수를 호출했기 때문에 문제없이 사용할 수 있었다.
댓글 없음:
댓글 쓰기