문자열 뒤집기가 주는 의의

우리나라의 컴공 교육에 대한 생각이 들게 하는 면접

저도 떡밥 덥썩 물어보렵니다.



지금 IT밸리에서 거주하고 있는 개발자 및 개발자 지망생, 그리고 저 같은 학생 분들께서 위의 사태(?)에 대해 극히 우려를 표명하고 나섰습니다.

사실 저도 이 글을 보고 참 문제스럽다.. 싶긴 했었지만.

존경하는 스승님(?)께서 들려주신 면접 사례를 보아서는 이정도는 가능하기도 하겠다.. 했습니다. 뭐 일단 이건 차차 다시 살펴보도록 하구요.


예전에 한번 만들어 본 적이 있긴 하지만(물론 수업시간 중이었지만) 저도 한번 생각난김에 그냥 대충 끄적끄적 해봤습니다.



뭐, 어쨌든 저도 아직 이쪽 바닥에선 어린아이이기 때문에 뭐라고 크게 말은 못하겠습니다만 보고 느낀바만 읊조려 보자면..


C 프로그래밍에 있어서 스트링 관리는 하나의 단원으로 분류될만큼 중요한 내용중 하나입니다. 그도 그럴것이 가장 직관적으로 메모리에 대해서 관리를 할 수 있는 부분이 아닌가 싶습니다. 배열이나 포인터를 통해서 메모리 관리가 가능하지만 이는 메모리 구조를 직접 눈으로 보기 전까지는 사실 좀 개념적인 느낌이 강하기 때문이죠.(디버거를 통해 메모리 구조를 볼 수 있지만 간단하게 C만 배운다고 가정하고 디버그 기법은 제외하겠습니다)

C에서는 문자열 처리가 곤란하기 때문에 char 형 배열을 문자열로 사용을 하는데 이로인해 스트링은 배열과 포인터의 집대성이라고 볼 수 있지요. 게다가 사실 문자열 입출력은 어플리케이션에 있어서 없어서도 안될 부분이기도 하고...


그래서 string함수를 배우는 중에도 strcpy나 strcat같은 함수를 직접 구현하도록 연습을 시키는 것이겠죠.


지금 나온 뒤집기 역시 마찬가지입니다. 굳이 포인터의 개념까지 알 필요도 없습니다. 배열에 대한 기초적인 지식과 반복구문에 대한 문법적인 이해만 알고 있어도 쉽게 해결을 볼 수 있는 문제입니다.

먼저 이 문제를 풀기위해선 코드를 떠올리기보단 알고리즘(순서도)를 떠올리는게 맞지 않나 싶습니다. 결국 코드라는 것은 인간의 머리에서 나오는 것이고 그것이 구현되는 것이기 때문이죠.


문자열을 뒤집는 것에는 대충 생각해봤을땐 두가지 방법이 떠오르는군요.



먼저 우리가 눈으로보고 문자열을 거꾸로 만드는 것, 즉 문자열을 뒤에서부터 앞으로 읽어오는 것입니다.

하지만 이 방법은 기존의 문자열만을 가지고는 완전하게 뒤집을 수가 없습니다. 왜인고 하니

A B C D E
이런 문자열이 있었을 때, E의 문자열을 맨 앞에 기록하기 위해 첫번째 위치에 엎어버리면

E B C D E
가 되어버리겠죠. 그래서 이 방법을 사용하기 위해선 별도의 문자열을 저장하기 위한 공간이 필요합니다.

이 공간을 만드는 방법에는 또 두가지가 있겠죠. 임의의 크기를 가진 char형 배열 변수를 만들든가, char형 포인터 변수를 선언하여 동적할당(malloc)하여 만드는 방법이 있습니다.

그러나 임의의 크기를 가진 char형 배열은 크기가 제한되므로 문자 오버플로우가 발생할 수 있습니다. 따라서 권할 수 있는 방법은 아니겠죠. 결국 포인터 변수를 하나 만들어서 동적할당 시켜야 합니다.

void my_strrev(char *str)
{
  int i, length;
  char *revstr;

  length = strlen(str);
  revstr = (char *)malloc(sizeof(char) * length + 1);   // 뒤의 +1은 NULL문자를 넣어주기 위한 것이란거 다 아시죠?
  
  for(i=0; i<length; i++)
    revstr[i] = str[length - 1 - i];       // 원본 문자열의 뒤에서부터 차례대로 값을 넣어줍니다.
  revstr[i] = '\0';               // 문자열의 끝에 NULL문자를 넣어줍니다.

  strcpy(str, revstr);
  free(revstr);
}


하지만 이 코드가 그렇게 마음에 들어보이진 않군요.

malloc함수나 strcpy를 사용해서 함수 호출이 잦은 편이잖아요. 게다가 깜빡하고 free라도 잊어버린다면.. 어휴.. 물론 저렇게 안하면 안되는 경우라면 모를까 더 좋은 방법이 있다면 더 간단하게 만들 수도 있겠죠.


다른 방법으론 swap을 통해 문자열의 위치만 바꾸어주는 것이 있겠죠. 전 개인적으로 이 방법으로 풀긴 했습니다만..

A B C D E
라는 문자열이 있다면, 가장 처음의 문자와 가장 뒤의 문자를 교체해 주는 것입니다.

E B C D A
가 되겠죠. 그럼 그 두번째 문자와 뒤에서 두번째 문자를 교체해줍니다.

E D C B A
이렇게 말이죠. 결국 요약하면 첫번째와 교환할 문자는 뒤에서 첫번째, 두번째와 교환할 문자는 뒤에서 두번째가 되는 식으로 규칙을 알 수 있죠.


가운데 글자야 변화가 필요가 없겠죠. 또는 가운데에서 가운데로 움직여봤자 똑같은 위치이지요.

그래서 이를 코드로 구현해 보았습니다.

void my_strrev(char *str)
{
  int i, length;
  char temp;
  length = strlen(str);

  for(i = 0; i < length/2; i++){

    temp = str[i];
    str[i] = str[length - i - 1];
    str[length - i - 1] = temp;
  }
}


간단합니다! for문의 조건이 복잡한 것도 아니고 for문 내부의 swap구문이 어려운 것도 아닙니다. 그저 단순히 머리속으로 생각한바를 코드로 그대로 옮기기만 하면 됩니다. 외부 함수라고는 strlen하나 뿐이고 그것조차 어려운건 아니니 위의 코드에는 어렵게 생각할만한 코드는 전혀 없는 것 같습니다.

length - i -1.. 이런 표현이 어렵다고 생각하실 분이 있을지도 모르겠습니다만 그냥 그거잖아요. 뒤에서 부터 숫자 빼는거..

물론 저기서 temp 임시 char형 변수를 사용하지 않고, for문안의 3줄을

str[i] ^= str[length - i - 1] ^= str[i] ^= str[length - i - 1];

라는 식으로 한줄로 요약도 가능합니다만 이건 가독성이 좀 떨어지죠? 이 표현이 swap을 할 때 사용한다는 것을 아는분이 적은만큼 혼자 볼 코드가 아니라면 알기 쉽게 쓰는 것도 코드를 작성하면서 가져야 할 자세 중 하나겠지요.



어쨌든 결론적으로 이 문자열 뒤집기 문제가 주는 의의는 바로 사고력인 것 같습니다.

컴퓨터 프로그램은 스스로 생각을 하고 답을 내어주지 않습니다. 어떠한 문제를 해결하기 위해서는 개발자가 문제의 푸는 방법을 생각해 보고 이를 코드로 옮긴 것일 뿐이니까요. 그래서 인간이 풀 수 없는 문제는 컴퓨터도 풀 수 없다라는 말이있겠죠.

이런 문제를 푸는 방법은 정말 다양한 방법이 있긴 합니다만(실제로 현재 저 글의 트랙백과 리플에 달려있는 답만 보더라도 꽤나 다양하죠. 풀이 방식은 비슷하다 손 치더라도.. 접근방법은 조금씩 차이가 있습니다) 그 중에서도 효율적이거나 합리적인, 혹은 이해하기 쉬운 방법을 택하는 것이 중요한 것입니다.

그보다 앞서 중요한 것은 아까 말씀드린 사고력, 즉 문제 해결 능력이라고 볼 수 있겠죠. 문자열을 뒤집어라! 라고 문제를 냈을때는 이 사람이 그 코드를 짤 수 있느냐 하는 능력을 보는 것도 있지만 그보다는 이 문제를 얼마나 논리적으로 접근하고 해결할 것인가를 보는 것이 더 크지않나 싶습니다.

접근만 올바로 이루어진다면, 그 다음부턴 이걸 배열로 구현하든 포인터로 구현하든 임시 변수를 쓰든 동적 할당을 하든 문제가 되지 않는 것이지요. 단, 어떤 방법을 쓰든 문법적 오류가 발생해서는 안되겠지만요.


결국은 머리를 써야합니다. 항상 어떤 문제를 풀거나 코드를 구현하기 전에 웹에 어떤 코드가 올라와있나, 어떤 레퍼런스가 있는가를 찾아보는 것이 아니라 나라면 이 문제를 어떻게 접근하고 해결할 것인가를 생각해 본 후 대략적이라도 그 방법이 떠올랐다면 구현을 시도해보든가, 다른 참고자료를 보고 분석하는 것이 현명한 학습방법이 아닌가 하고 생각을 해 봅니다.

by 리스 | 2009/05/21 13:06 | Developer | 트랙백 | 핑백(1) | 덧글(17)

트랙백 주소 : http://lesstopia.egloos.com/tb/4953124
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Linked at ZeroNucleus : 문자.. at 2009/05/23 03:02

... 어서 한번 풀어보았다. 자세히는 모르겠습니다만. 저도 한번 풀어보았습니다. [관련 글] http://lohengrin.egloos.com/1907690 http://lesstopia.egloos.com/4953124 [문제] 문자열 뒤집기. : int main() { char str[] = ... more

Commented by 우주인 at 2009/05/21 13:20
뻘리플 같지만... 코드를 어떻게 저렇게 깔끔하게 올리시나요? 들여쓰기가 다 엉망이 되던데...
Commented by 리스 at 2009/05/21 13:21
아.. ㄱ을 누르고 한자버튼을 누르면 1번이 전각 공백입니다. 그래서 그걸 두번 입력하면 4칸 공백 생기는 탭을 주는 효과가 난답니다 :D

제가 웹 초반 시절부터 즐겨쓰던 들여쓰기 기법(이라고 할 것도 없지만..)입니다.
Commented by 하뉴 at 2009/05/21 13:27
오오, 그런방법이 있었군요. (ㄱ 한자 1) x2 오오.
무식한 스페이스 연타로 들여쓰기를 했는데, 그런 좋은 방법이!
Commented by 하느바람 at 2009/05/21 13:59
원칙적으로는 <pre> </pre> 테그로 감싸면 공백이라던지 가 전부 그대로 출력되는게 맞습니다. [이글루에서도 되긴하네요]
그리고 스페이스 공백의 경우는 &nbsp;로 넣으시면 연속공백을 넣을 수 있긴합니다만 번거롭지요.
메모장에 코드를 쓴다음 " " 를 "&nbsp " 로 바꾸면 가능할거 같지만 해본적은 없구요.
원래 HTML에는 <code></code> 라는 코드 출력용 테그가 있긴합니다만. 이게 서식화출력이 포함되는지는 잘 모르겠네요.
Commented by 리스 at 2009/05/21 14:01
하느바람// pre태그를 쓰는 방법도 있긴 하지만.. 예전에 메모장으로 홈페이지 만들던 때 pre를 쓰니 문자출력설정이 미묘하게 바뀌는 것 같아 손이 안가더군요;;

code태그는 처음보는군요. 한번 찾아봐야겠습니다.
Commented by 연비 at 2009/05/21 13:47
헤에........가운데는 그대로.........'ㅡ'............
Commented by 리스 at 2009/05/21 13:48
for문의 조건문이 <가 되느냐 <=가 되느냐에 따라 가운데를 건드리냐 안건드리냐가 되긴 합니다만.. 어차피 바뀔 필요가 없다면 굳이 건드릴 필요도 없겠지요.
Commented by 연비 at 2009/05/21 13:51
가운데는 그대로라 그걸 생각도 해봤지만..........그냥 대충대충 끄적끄적 저는 짯네요 ;;
........그냥 학교 다닐때는 재사용성의 뭐졍? 효율성이 뭐졍?
이러고 그냥 돌아가게만 만들자!가 목표였는데
지금은..........아흑 그런 마인드로 코딩을 했던게 엄청나게 후회되고 있습니다 ㅠㅠ

회사에서 재사용성 안기를래? 깔끔하고 보기편하고 좋게 안짤래? 하고 마구 혼나융 ㅠㅠ
Commented by 리스 at 2009/05/21 13:53
ㅎㅎ 저도 지금 배우면서 재사용성을 매우 강조하시는 분이 계셔서..

클래스 대충 짰던 것도 재활용 하려고 염두에 두고 만들려면 차라리 다시 만드는게 낫기도 하겠더군요. 이래서 디자인 패턴을 제대로 파악을 해야하는데..
Commented by 연비 at 2009/05/21 16:02
그런데 두번째 소스 문자가 홀수 이라면 상관없을텐데 짝수면 어떠헤 되는건가융 ;ㅁ;?
Commented by 리스 at 2009/05/21 16:23
예를 들어 A B C D가 있다면,
D B C A => D C B A가 되겠죠.

원리는 앞에서 1번째 글자와 뒤에서 1번째 글자의 교환, 앞에서 2번째 글자와 뒤에서 2번째 글자의 교환, ~ 앞에서 n번째 글자와 뒤에서 n번째 글자의 교환.. 이런 식이니까요.
Commented by 연비 at 2009/05/21 16:26
아 어떻게 보면 퀵정렬이나, 버블정렬이랑 비슷하네요 'ㅅ'
끄덕끄덕 이해 했어융 ㅋ
Commented by 리스 at 2009/05/21 16:31
완전히 반대로 된 수열[문자열]을 차례대로 만든다는 의미에선 정렬이라고 할 수 있겠죠 ㅎㅎ

정렬 알고리즘까지 생각해보면 정말 방법은 무수히 많은 것 같군요..
Commented by 玄月夜 at 2009/05/21 17:51
....이살암, 이런글을 올리고있어어어어어

[접근할 수 없다]
Commented by 리스 at 2009/05/21 17:56
네이버보다는.. 이쪽에서 올리는게 더 맞는거 같아-ㅅ- 네이버는 검색만 낼름하고 가는 사람들이 많은 반면 여긴 나름 토론도 잘되고..<-
Commented by kurome at 2009/05/22 12:23
strrev(str) 끝..
Commented by 리스 at 2009/05/22 13:50
물론 만들어진 함수를 사용할 수는 있겠습니다만.. 여기서 초점을 맞추는 것은 그것의 작동 원리를 알자는 것이니까요.
사실 그렇게 치자면 자료구조[리스트, 스택, 큐 등등]도 알 필요가 없는 것이 이미 라이브러리에서 제공되고 있는 것을 사용하면 되는 것이니까요.

그렇지만 그것을 과연 안다, 라고 할 수 있을지는 모르겠습니다. 물론 이런 것들을 모두 익혔다고 전제하고 있는 상황이라면 제공되고 있는 라이브러리를 쓰는것이 합당하겠지만 말입니다.

여기서 또 변수로 작용할 수 있는 것이 이를테면 strcpy같이 보안에 취약한 라이브러리 함수가 있다면 과연 이것을 사용하는 것이 타당할 것인가.. 하는 의문을 제기할 수도 있겠죠.

:         :

:

비공개 덧글

◀ 이전 페이지다음 페이지 ▶