서론
C 또는 C++를 배우면서 하나의 장벽이 있다고 한다면 바로 포인터입니다.
이 개념은 C/C++를 배우기 전 컴퓨터의 구조에 대해 공부를 해야만 이해할 수 있는 부분이기 때문에 그에 관한 간단한 상식이 없다면 절대로 쉽게 이해하고 받아들이기 힘든 부분 중 하나입니다.
주소로 접근한다... 이게 뭥미...? 라는 생각이 머릿속을 떠나지 않게 되고 결국 C/C++에서 멀어지게 만들죠.
그래서... 라기 보다는 이 포스트에서는 포인터를 설명하기보다는 하나의 예를 들며 장점을 말씀드리고자 합니다.
이 외에도 포인터의 특성을 이용하여 구현할 수 있는 다양한 방식이 있기 때문에 하나하나 조목조목 설명드리기는 힘들겠네요.
본론
아래와 같은 클래스를 하나 만들었습니다.
class Dynamic
{
int a, b, c, d, e, f, g, h, i, j, k, l, m, n;
int gj,aerthg,aerdthg,aerhg,aer;
int hetr,hsd,th,jhnty,hsrt,trh,htr,dfg,fd,s;
public:
Dynamic(int i){;}
~Dynamic(){}
};
보시는 바와 같이 별다른 기능은 없이 int형의 변수 수십개를 갖고있는 용량이 큰 클래스입니다.
그리고 위의 클래스 객체를 주고받는 함수도 만들었습니다.
Dynamic funcVal(Dynamic a)
{
return a;
}
Dynamic& funcAlias(Dynamic& a)
{
return a;
}
Dynamic* funcAdd(Dynamic* a)
{
return a;
}
하나는 객체의 값을 그대로 가져와 아무런 작업 없이 반환해주는 함수이구요
또다른 하나는 에일리어스를 이용하는 방식입니다.
마지막 함수는 객체의 포인터 값을 가져와 아무런 작업 없이 반환해주는 함수입니다.
이 세개의 다른 함수를 각각 천만번 수행시켜보도록 하겠습니다.
제가 작성한 메인 함수는 다음과 같습니다.
void main()
{
Dynamic a(1);
Dynamic* b = NULL;
DWORD start = 0;
DWORD end = 0;
start = GetTickCount();
for(int i=0 ; i<10000000 ; ++i)
{
a = funcVal(a);
}
end = GetTickCount();
cout <<"값의 반환 10,000,000회 수행시간 : " << (float)(end - start)/1000 << "초" << endl;
start = GetTickCount();
for(int i=0 ; i<10000000 ; ++i)
{
a = funcAlias(a);
}
end = GetTickCount();
cout <<"참조의 반환 10,000,000회 수행시간 : " << (float)(end - start)/1000 << "초" << endl;
start = GetTickCount();
for(int i=0 ; i<10000000 ; ++i)
{
b = funcAdd(&a);
}
end = GetTickCount();
cout <<"주소의 반환 10,000,000회 수행시간 : " << (float)(end - start)/1000 << "초" << endl;
system("pause");
}
매우 간단한 프로그램입니다만... 가독성은 별로입니다. 테스트 소스이므로 감안하고 봐주세요.
간략히 설명하자면 for문에 진입하기 전 시스템 시간을 저장하고
for문을 빠져나가면 다시한번 시스템 시간을 확인해서 최종시간과 시작시간의 차를 구하는 방식으로 테스트를 진행했습니다.
거두절미 하고 결과만 보도록 하겠습니다.
먼저 디버그 모드입니다.
흐음... 흥미롭군요?
값에 의한 전달은 1.357초가 걸렸고 참조는 0.437초 주소는 0.25초가 걸렸습니다.
제 컴퓨터가 그렇게 좋은건 아니기 때문에 더 좋은 컴퓨터에서는 더 빠른 결과를 보실 수 있으실 겁니다.
그럼 릴리즈 모드에서의 결과도 한번 보도록 하겠습니다.
오호라...
시스템 시간으로는 측정이 안될정도로 빠른 아래 두개에 비해 값에 의한 전달은 0.561초를 기록했습니다.
결론...?
왜 이런 결과가 나오게 되었는지는 힌트만을 하나 드리도록 하겠습니다.
위에서 만든 클래스는 29개의 int형 변수를 갖고있습니다. int형 변수 하나가 4바이트로 구성되어있으니까 29개면...
116바이트로군요. 이렇게 구성된 객체를 천만번 전달하고 반환받고 하게 됩니다.
그러나 주소에 의한 전달방식은 객체의 용량에 관계없이 그 값이 저장된 주소를 전달하고 전달받게 됩니다. 현재 통용되는 32비트 주소체계의 주소는... 1바이트당 8비트로 구성되어있으므로 4바이트가 됩니다. 즉 클래스의 멤버가 int 29개 뿐만 아니라 100개 10000개가 되더라도 주소로 전달하면 4바이트만 전달된다는 말입니다.
이정도 힌트라면 답을 쉽게 구하실 수 있으실거라고 알겠습니다.
지금까지 포인터를 이용하여 얻을 수 있는 장점 중 하나를 파헤쳐보고 실험을 통해 알아봤습니다.