지금의 내 팀장님이 기술면접때 내주셨던 문제.

이외에도 많은 게임 기업 필기테스트(판교에서 건물 제일 좋은 기업)에 등장했던 문제이다.


처음 이 문제를 들었을 때는 크게 어렵지 않았다. 

왜냐하면 내 스승님이신 '코딩인터뷰 퀘스쳔' 책에 이에 대해서 짧지만 충분하게 설명해두었기 때문이다.


아주 단순한 비교로는 malloc()함수이고 new연산자이다.

단순하지만 중요하지 않다는 것은 아니다.


결국 위의 말이 정답이고 본질이기 때문이다.


그리고 이 질문하나로 기술면접에서 수없이 많은 질문들로 뻗어나갈 수 있다.


그 당시의 내 대답은 이랬었던 것 같다. 

음 ... (생각좀 가다듬는 시간) malloc()은 함수이고 new는 연산자입니다.

그래서 발생하는 가장 큰 차이는 생성자의 유무입니다. malloc()은 시스템 함수로서 함수 안에서 메모리를 할당하지만

new는 연산자로 바로 메모리를 할당하는게 아니라 생성자를 호출하여 메모리를 할당합니다. 그러므로 생성자를 통하여

호출하기 때문에 new로 메모리를 할당하면 생성 시 초기화가 가능한 장점이 있습니다.


이렇게 대답하고 굉장히 뿌듯했다. 그리고 추가타로 들어올 질문 역시 조금은 대비되어 있었다.

위에서 언급한 판교에서 건물 제일 좋은 게임 기업에서 들어온 후속 질문으로 그럼 malloc() calloc() realloc()에 대해서

말해보고 realloc()시 발생할 수 있는 문제점에 대해서 언급하라는 질문을 들어봤기 때문이다.


그래서 대답 후 미리 머리속으로 malloc()과 calloc() realloc()에 대한 차이점을 정리중이었다.

그리고 realloc()의 대표적 문제점에 대한 답변도 준비하고 있었다.


realloc()의 대표적 문제는 정말 혹시나 메모리 할당이 실패할 경우 null이 반환되기 때문에 기존의 메모리가 할당되어

있는 포인터를 잃어버리는 것이다.


예를들어 

int * mem = malloc();

mem = realloc(); //실패 시 mem에는 null값 존재


그러므로 기존에 할당했던 mem이 free() 되지 않고 이제는 찾을 수 없는 곳으로 가버린 것이다.

당연히 이런 점은 메모리 누수로 남는다. 그리고 이런 코드가 (물론 그럴 일 없겠지만) 하나의 프로그램에서

화면을 Update하는 GUI쪽 함수라면 순식간에 메모리가 터져버린다.


그래서 realloc()을 할 때는 기존의 메모리 주소를 저장하고 실패 시 복구하는 프로세스가 함께 있어야 한다.


int * mem = malloc();

/* 

... process - realloc 필요

*/

// 기존 메모리 주소 백업

int * mem_temp = mem;


// 메모리 재할당

mem = realloc();


// 복구 과정

if( mem == null ) {

mem = mem_temp;

}


속으로 그러면서 드는 생각.

결국 질문은 다 돌고 도는구나. 


실제 코딩을 하고 지켜보지 않는 이상 기술면접으로 물어볼 수 있는 질문들은 어쩔 수 없이 한정되어 있나?

이런 생각이 찰나간 들었다.


그리고 자신만만하게 질문을 기다리고 있는데 팀장님이 말씀하셨다.


그 ... 생성자에서 생성 시 초기화를 한다고 했는데 그건 어떻게 하는건가?


...


???


우의잉읭???


이게 무슨 소리지. 뭘 어떻게 초기화하는가. 그냥 하면 되는거 아닌가?

순간적으로 혼란스러웠다.


왜냐하면 이때의 나에겐 초기화 리스트대입의 차이를 모르고 있었다.


그래서 이렇게 답변드렸던 것 같다.

클래스에 선언한 여러 변수들은 개수가 여러 개일수도 있고 각각의 타입이 다를 수 있기 때문에

생성자 안에서 각각 타입별로 필요한 값으로 초기화 할 수 있습니다. 

예를들어 int형이면 0으로 초기화하고 포인터 타입이면 null로 초기화하듯이 말입니다.


그러고 팀장님 표정을 살폈다.

찌뿌리신다.

차가워지셨다.


망했다.


그런 생각이 들었다.


자신만만해하더니 또 이꼴이냐?


순식간에 자괴감에 빠졌다.

그리고 팀장님은 더이상 묻지 않으셨다.


이후 면접이 끝나고 나는 굉장히 찝찝했다. 

무난히 흘러간 면접이라고 생각했는데 답변하지 못한 찜찜했다.


하지만 다행히 운이 정말 좋아서 통과되었고 지금은 이렇게 함께 팀장님과 일하고 있지만

회사 들어와서 팀장님과 Effective C++ 시리즈를 함께 공부하면서 그때 면접때 팀장님이 말씀하신 부분을 알게되었다.


간단하게 초기화리스트로 표현하는 이 부분은 두 단어로 표현이 가능하다.

생성 시 초기화와 생성 후 초기화다.


그리고 보통 우리가 배울 때 하는 초기화가 바로 생성 후 초기화다. (물론 잘 배웠으면 아니겠지만)


Car* car = new Car();

했을 때 보통 생성자를 만들면 이렇다.


class car {

private:

int 바퀴;

int 엔진;

int 기름;


public:

// 생성자

Car();

}

란 차의 클래스가 있을 때


Car() {

바퀴 = 4;

엔진 = 1;

기름 = 0;

} 은 생성 후 초기화이고


Car() : 바퀴(4), 엔진(1), 기름(0) {

} 은 생성 시 초기화이다.


뭔 차이일까?


솔직히 지금도 이렇게 초기화하는 것이 그렇게 중요한가? 이런 생각이 많이든다.

생성 후 초기화보다 생성 시 초기화가 당연히 효율은 좋다.


왜냐하면 생성 시 초기화는 한 흐름에 모든 것이 끝나고, 생성 후 초기화는 생성과 초기화 2가지 흐름으로 처리되기 때문이다.

그러니 비용이 당연히 2배이긴 하지만 ... 요즘의 컴퓨팅 파워로는 충분히 감당할 수 있는 수준이 아닌가?

라는 생각이 배우면서 많이 들었다.


하지만 일하다보니 정말 사소한 부분이라고 생각했던 곳에서도 효율의 차이가 많이 난다.

작은 곳에서부터 꼼꼼히 하는 습관이 정말 중요한 것을 느낀다.


그리고 지금의 컴퓨팅 파워로도 너무나 느리다고 생각될 때가 많다.


그러니 코드 한 줄 추가되는 것없이 효율적일 수 있는 부분이 있다면 그렇게 고치고 수정하는 것이 옳다.

어쩌면 팀장님은 그런 부분도 간과하지 않고 기억하고 있는 성격인지 보려고 했을 수도 있다.


- 세 번째 면접 일기 끝


+ Recent posts