프로그래밍은 언제나 숫자와의 경쟁인 것 같다. 반올림이 되느냐, 부동소수점이냐, 정수 오버플로우(integer overflow) 등은 백발이 되어 코드를 만질 때 까지 항상 따라다니는 문제가 될 것이다.

MySQL 날짜 관련 이슈

얼마 전 필자가 다니는 회사에서 발생한 데이터베이스 관련 이슈로 다음과 같은 문제가 발생하였다. 아래는 MySQL 관련 문제에 대하여 공유된 내용이다.

MySQL 5.6.4 부터 시간값 저장시 밀리세컨드를 지원한다. 하지만 DATETIME의 경우 길이가 6일 경우에만 가능하다. 그런데, DATETIME 타입(이는 DATETIME(4)와 같다)일 경우 밀리세컨드 부분을 반올림(round)하는 버그가 있다. #68760
이 버그로 인해 1999년 12월 31일 23시 59분 59초 500ms 같은 값을 DATETIME 타입에 입력하면 반올림되어 2000년 1월 1일 0시 0분 0초 0ms 가 돼 버리는 현상이 발생한다.

MySQL Community Server 의 C 언어 소스코드를 보면 날짜와 관련된 데이터를 다루는 세 가지가 데이터 타입이 있다. MYSQL_TYPE_DATE, MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIMESTAMP.

// MySQL 데이터 타입  
/*****************/  
#define CLIENT_MULTI_QUERIES    CLIENT_MULTI_STATEMENTS      
#define FIELD_TYPE_DECIMAL     MYSQL_TYPE_DECIMAL  
#define FIELD_TYPE_NEWDECIMAL  MYSQL_TYPE_NEWDECIMAL  
#define FIELD_TYPE_TINY        MYSQL_TYPE_TINY  
#define FIELD_TYPE_SHORT       MYSQL_TYPE_SHORT  
#define FIELD_TYPE_LONG        MYSQL_TYPE_LONG  
#define FIELD_TYPE_FLOAT       MYSQL_TYPE_FLOAT  
#define FIELD_TYPE_DOUBLE      MYSQL_TYPE_DOUBLE  
#define FIELD_TYPE_NULL        MYSQL_TYPE_NULL  
#define FIELD_TYPE_TIMESTAMP   MYSQL_TYPE_TIMESTAMP  
#define FIELD_TYPE_LONGLONG    MYSQL_TYPE_LONGLONG  
#define FIELD_TYPE_INT24       MYSQL_TYPE_INT24  
#define FIELD_TYPE_DATE        MYSQL_TYPE_DATE  
#define FIELD_TYPE_TIME        MYSQL_TYPE_TIME  
#define FIELD_TYPE_DATETIME    MYSQL_TYPE_DATETIME  
#define FIELD_TYPE_YEAR        MYSQL_TYPE_YEAR  
#define FIELD_TYPE_NEWDATE     MYSQL_TYPE_NEWDATE  
#define FIELD_TYPE_ENUM        MYSQL_TYPE_ENUM  
#define FIELD_TYPE_SET         MYSQL_TYPE_SET  
#define FIELD_TYPE_TINY_BLOB   MYSQL_TYPE_TINY_BLOB  
#define FIELD_TYPE_MEDIUM_BLOB MYSQL_TYPE_MEDIUM_BLOB  
#define FIELD_TYPE_LONG_BLOB   MYSQL_TYPE_LONG_BLOB  
#define FIELD_TYPE_BLOB        MYSQL_TYPE_BLOB  
#define FIELD_TYPE_VAR_STRING  MYSQL_TYPE_VAR_STRING  
#define FIELD_TYPE_STRING      MYSQL_TYPE_STRING  
#define FIELD_TYPE_CHAR        MYSQL_TYPE_TINY  
#define FIELD_TYPE_INTERVAL    MYSQL_TYPE_ENUM  
#define FIELD_TYPE_GEOMETRY    MYSQL_TYPE_GEOMETRY  
#define FIELD_TYPE_BIT         MYSQL_TYPE_BIT  

날짜가 포함되는 데이터 타입에 대해 코드를 분석해 본 결과 날짜 데이터를 조작하여 저장하는 코드를 찾지 못했다. 꼼꼼하게 분석한 것은 아니기 때문에 혹시 놓친 부분도 있을 거라고 생각한다.

MySQL Server에서 문제를 발견하지 못했다면,다음 봐야 할 것이 MySQL Client와 MySQL Connector/J 소스 코드인데, MySQL Connector/J 에서 이슈와 직/간접적으로 관련된 코드를 발견하였다.

MySQL Connector/J

MySQL Connector/J의 소스 코드에서 /src/com/mysql/jdbc/TimeUtil.java 파일에서 한 가지 이슈를 재연할 수 있는 코드를 발견했다. 메서드 이름  public static Time changeTimezone(…) 메서드를 살펴보면 클라이언트와 서버 간에 TimeZone이 다른 경우 이를 보정하는 연산이 포함된다.

다음은 TimeZone.java의 코드의 일부분이다.

Calendar fromCal = Calendar.getInstance(fromTz);  
fromCal.setTime(tstamp);  

int fromOffset = fromCal.get(Calendar.ZONE_OFFSET)
               + fromCal.get(Calendar.DST_OFFSET);  
Calendar toCal = Calendar.getInstance(toTz);  
toCal.setTime(tstamp);  

int toOffset = toCal.get(Calebndar.ZONE_OFFSET)
              + toCal.get(Calendar.DST_OFFSET);  
int offsetDiff = fromOffset - toOffset;  
long toTime = toCal.getTime().getTime();  

if (rollForward || (conn.isServerTzUTC() && !conn.isClientTzUTC())) {  
    toTime += offsetDiff;  
} else {  
    toTime -= offsetDiff;  
}  

Timestamp changedTimestamp = new Timestamp(toTime);  

만약, 클라이언트와 서버 사이에 0.001ms 시간 차이가 생긴다면, TimeZone은 13이라는 값의 차이가 발생한다. 클라이언트와 서버의 시간 차이 기준은 MySQL Server에서 열린 세션(session)을 기준으로 판단한다. 혹시나 클라이언트와 서버의 TimeZone이 상이하게 설정된 경우 어마 어마한 시간 차이가 생기게 된다.

다음과 같이 필자가 의도적으로 0.001ms 차이가 발생하는 경우 MySQL Connector/J 가 DateTime을 교정하는 것을 확인하였다. (단, getTimeFast 메서드는 MySQL Connector/J 에 포함된 메서드임)

Calendar calendar = Calendar.getInstance();  
calendar.set(1999, 12, 31, 23, 59, 59);  
calendar.setTimeInMillis(nanos);  

System.out.println(getTimeFast(0, bits, 12, 14, calendar, tz, true));  

// output  
2000-01-01 00:00:00.0  

결론

MySQL 날짜 관련 이슈의 직접적인 발생 원인은 찾지 못했다. 하지만 MySQL Connector는 MySQL Client와 쌍으로 움직이니 간접적으로 영향을 미칠 수 있을 것이다.

현재까지 살펴본 바로는 가장 가능성이 있는 이 이슈/버그는 MySQL Connector/J와 MySQL Client일 가능성이 가장 크고, MySQL Server 내부 버그일 가능성은 다소 낮다고 본다. 이 이슈/버그 밑에 코멘트에 MySQL .NET Connector를 사용하는 경우도 문제가 있다고 하니.. 어쨌거나 저쨋거나..

Posted by 땡초 POWERUMC

댓글을 달아 주세요

[GDB] Hopper Disassembler 앱

C++ 2014. 2. 12. 02:21 |

필자는 소프트웨어 개발에 필요한 앱은 라이선스를 구매해서 쓴다. 자랑은 아니지만 앱스토어와 그 외 구매한 앱의 개수가 200개가 넘어간다. 히힣; 오늘 소개하는 앱 처럼 제값어치 하는 앱에겐 뭔가 모를 희열을 느낀다. 여성분들이 쇼핑을 좋아하는 것처럼 -0-

Hopper Disassembler

이 앱은 윈도우의 WinDbg, 그리고 GNU gdb처럼 디버거 명령줄(Command Line)에 익숙하지 않은 디버거에게 유용한 앱이다. 몇 달 전, Objective-C with LLVM 어셈블리 코드를 분석하기 위해 구매했다. Objective-C 내부적인 언어적 매커니즘을 이해하기 위해 역어셈블리 분석은 필수이기 때문이다. 그 중 이 앱은 기대한 것 보다 기능은 적지만, 사소한 곳에서 감동을 주는 앱이라 생각된다.

역어셈블리 앱 중 윈도우에선 OllyDbg 가 있다면, 맥에서는 바로 이 Hopper Disassembler 가 있다. OllyDbg는 작지만 기능에 충실한 서민적인 앱이라면, Hopper Disassembler는 비주얼적인 면이 좀 더 부각된 매니아 앱 같다. 기능적인 면에서 Hopper Disassembler가 조금 떨어지긴 하지만, 맥에서 이 정도면 충분히 쓸만하다고 판단된다.

특히 Hopper Disassembler는 윈도우, 맥 그리고 리눅스까지 지원하는 효자 앱이다. 이 세 가지 운영체제 버전을 모두 구입해도 12만원 정도면 된다. 더 고가의 프로페셔널한 상용 버전에 비하면 저렴한 편에 속한다. 필자처럼 맥, 리눅스, 윈도우, 이렇게 여러 운영체제를 사용한다면 Hopper Disassembler는 충분히 매력적이다.

앱 훑어보기

Hopper Disassembler 런타임(runtime) 디버깅을 하려면 전용 디버거 서버 앱을 사용해야 한다. 그러니까 클라이언트-서버 구성으로 Remote(or Pipe) 통신으로 디버깅을 진행하는데 이 인터페이스가 썩 좋지만은 않다.

클라이언트/서버로 구성

아래의 스크린샷 처럼 메인 윈도우, 서버 에이전트, 어셈블리 레지스터 윈도우 이렇게 세 개를 띄어야 한다. 멀티 모니터에서는 장점이 될 수도 있고, 싱글 모니터에서는 단점이 될 수 있다.



비주얼 및 기타

디버거 앱 답지 않게 좀 더 다양한 색상을 사용하였지만, 거부감 없이 알아보기 더 쉽다. Flow Graph와 Pseudo Code도 비교적 깔끔하다. 초기에 사용할 일이 잦지만 Objective-C의 기계어(?) 코드 스타일에 익숙해 지다보면 거의 사용하지 않는 번들 기능 중 하나다. Objective-C with LLVM 구성인 경우 .NET과 Java와 같이 가상머신(virtual machine)에서 동작하지만, 이보다 좀 더 하위 레벨인 LLVM 컴파일 바이너리는 거의 일관적으로 유사성이 있기 때문이다.

검색 기능과 참조된 주소를 분석하는 기능은 기대 이상으로 빠르고 정확하다. 특히 call, je, jne, jmp 그리고 meta data, symbols, 마지막으로 xref 등을 쉽게 찾아가거나 검색할 수 있다. xref의 참조 주소의 원본을 툴팁으로 보여주는 등 예상하지 못한 곳에 꼼꼼한 비주얼을 보여준다.

눈안의 가시, 버그

일부 코드를 디버깅 하다보면 특정 인스트럭션(instructions)에서 Step Over를 Step Into로 동작하는 경우가 발생한다. 이게 은근히 신경을 곤두세우는데 아직까지 별다른 패치나 업데이트가 없고, 앞으로도 없을 것 같다. 필자는 이 버그와 앱의 가격과 협상했지만, 보다 세세한 디버깅을 제어해야 한다면 다른 앱(gdb? -_-)을 사용하는 것이 좋을 것 같다.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

최근 재미있는 글을 봤다. C 언어로 모바일을 위한 API 서버를 만들었는데, 이에 대해 댓글의 토론이 가관이 아니다. 물귀신들이 들러 붙고 난리도 아니다.

  1. 아파치 모듈로 개발된 API 서버, 이음 베이론을 소개합니다.
  2. C언어로 API 서버 개발, 생각보다 나쁘지 않아요

글의 결론은 ‘모바일 API 서버를 C 언어로 만드니 성능이 좋네요’.. 이에 대항하는 물귀신들은 ‘나를 납득시킬만한 근거를 대라’, ‘C언어로 만들었다고 자랑질이냐’ 등등…



모든 사람이 경험도 다르고, 깊이도 다르니 글쓴이야 모든 눈높이에 맞춰 대응하기도 힘들겠다. 그냥 나는 내 생각대로 얘기하자면…

  • 글쓴이가 모바일 API를 C언어로 만든 글은 필자가 보기에 자랑질도 아니고, 이런 사례를 만들었는데 놀랍더라.. 정도인 것 같은데, 글쓴이가 무슨 비윤리적인 짓을 한 것 마냥 댓글러들의 오두방정이 지나치다. C언어로 만들어서 만족한다는 데 왜 주변에서 C로 만들었냐고 오두방정일까… C언어로 만들면 우리나라 박대통의 창조경제에 어긋나기라도 하나?

  • DB 부하가 가장 많은 상태에서 C언어로 API 성능을 극대화 한 것 자체 무용론… 댓글러들은 무슨 ’하드웨어 스팩을 대라..!’는둥, 저렇게 차이가 나는데 ‘프로파일링을 해봤느냐.. 당연히 해봐야 하는거 아닌감?’ 하는 뉘앙스들… 글쓴이가 자기들 따까리도 아니고… 프로파일링 해봤는 지 궁금하면 직접 자바가 왜 느린지 프로파일링 해보는 게 더 빠르지 않을까 생각드는데…

  • C는 시스템 프로그래밍에 적합한 로우레벨, 자바나 트랜드한 스크립트 언어들은 고급언어… 정말 욕 한 바가지 하고 싶다. 정말 지랄 옆차기도 제대로 헛발이다. 초기에 어느 정도 프레임과 API가 정의되면, API 코딩하는 건어느 언어로 하던 그 표현력은 거의 비슷하다. 10줄 짜리 1줄로 표현한다고 생산성이 좋고, 유지보수, 리팩토링까지 좋은 건 아니다. 언젠가 요구 사항으로 인해 1줄 짜리를 10줄로 풀어낼 날이 올거다.

  • C/C++/Objective-C 등 C 계열의 언어로 만들어진 라이브러리가 없다고들 하는데 C 계열의 오픈 소스가 가장 많다. (근거는 직접 찾아보세요) 이제 슬슬 트랜드의 정점을 찌르는 파이썬, 루비, 자바스크립트 등의 스크립트 언어들의 오픈 소스를 다 합쳐도 C 계열을 추월하지 못한다. 물론 댓글러들의 눈높이에 맞는 C 계열 오픈 소스는 그리 많지 않을 뿐이다.

  • 모바일 API 서버에 대한 글이라 웹 개발자만 우루루 죽자고 달라 붙는다. C언어로 만든 API 서버가 자바 서버 10대를 5대로 줄인거면 좋은 성과 아닌가? 100대라면 산술적으로 무료 50대나 줄일 수 있는데도… C언어로 만들면 마치 유지보수 헬게이트라고 하는데, 필자는 JavaScript가 최고의 헬게이트 아닌가 뼈저리게 느낀다.

  • 나는 개발자들이 포지셔닝된 트랜드를 어느 정도 따라갈 수 있으면, 더 이상 죽자살자 트랜드를 쫓지 않길 바란다. 그래봐야 빨리 은퇴하는 길이고, 나이들면 더 할게 없어질거다. 트랜디한 기술은 치고 올라오는 귀염둥이 신입들에게 넘기고…

    그때가 되어도 귀염둥이들과 트랜디한 기술 가지고 ‘그거 써봤니?’, ‘써보니 어떻드라’, ‘버그 있더라’ 농담 따먹고 놀 순 없지 않는가…?
 
 본격적으로 개발을 하던 시점에서 트랜드는 천천히 따라가고, 점점 그 이전 기술을 공부할 필요가 있다고 본다. 소프트웨어 공학도 공부하고, C 언어, 그리고 어셈블리어, 그리고 운영체제까지… 그럼 댓글러들이 하는 토론보다 더 발전적인 이야기와 비전이 보일거다.


Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. jh.l 2014.01.23 06:59 Address Modify/Delete Reply

    언어는 언어일뿐

  2. jongwook 2014.01.23 11:06 신고 Address Modify/Delete Reply

    안녕하세요 귀여워해주셔서 감사합니다!

  3. sebatyler 2014.01.23 11:53 Address Modify/Delete Reply

    좋게 봐주셔서 감사합니다.

  4. 전호진 2014.01.23 15:16 Address Modify/Delete Reply

    승리한 병신이 되라!
    멋져요..

  5. snail 2014.01.24 01:12 Address Modify/Delete Reply

    분명 생산성 좋다는 언어인데도 코드를 스파게티로 만들어두는 사람 보면서 느낀게, 역시 잘 짜는 사람은 뭘 해도 이쁘게 짜겠구나더라고요..

  6. Tae-Ho 2014.10.14 18:09 신고 Address Modify/Delete Reply

    ㅎㅎ c를 괜히 저평가하려는 개발자들이 좀 있습니다. 그들에겐 이유란 없습니다. 이유가 있다면 자기가 안해봤다는거...

  7. 조제 2016.01.25 12:40 Address Modify/Delete Reply

    좋은글 잘 읽었습니다

재미있는 C 언어 문제

인터넷 RSS 피드를 구독해서 보다가 재미있는 C 언어 코드를 발견했다. 조금 난해하게 보이는 코드다. 이런 코드로 신입 개발자 면접 시험을 보면 재미있겠단 생각이 든다.

문제의 C 언어 코드는 다음과 같다. 소스 코드와 실행 결과가 전혀 매치가 안되는 이 또라이 같은 코드를 보고 순간 멈칫 할 것이다. 하지만 조금 귀 기울여 보면 말 되는 코드다.

이 링크를 클릭하면 즉시 컴파일 된 실행 결과도 함께 볼 수 있다.

main()
{
int a,b,c;
int count = 1;

for (b = c = 10; 
a = "- FIGURE?, UMKC,XYZHello Folks,\
TFy!QJu ROo TNn(ROo)SLq SLq ULo+\
UHs UJq TNn*RPn/QPbEWS_JSWQAIJO^\
NBELPeHBFHT}TnALVlBLOFAkHFOuFETp\
HCStHAUFAgcEAelclcn^r^r\\tZvYxXy\
T|S~Pn SPm SOn TNn ULo0ULo#ULo-W\
Hq!WFs XDt!"[b+++21]; )
for(; a-- > 64 ; )
putchar ( ++c=='Z' ? c = c/ 9:33^b&1);

return 0;
}

/* 실행 결과
                    !!!!!!                                                     
                    !!!!!!!!!!                                                 
                     !!!!!!!!!!!!!!!                                           
                       !!!!!!!!!!!!!!                                          
                     !!!!!!!!!!!!!!!                                           
                      !!!!!!!!!!!!                                             
                      !!!!!!!!!!!!                                             
                        !!!!!!!!!!!!                                           
                        !!!!!!!!                                               
                        !!!!!!!!!!                                             
                       !!!!!!!!!!!!!!                                          
                     !!!!!!!!!!!!!!!!                                          
                    !!!!!!!!!!!!!!!!                                  !!!!!    
                  !!!!!!!!!!!!!!!!!!!                               !!!!!!!!!! 
                 !!!!!!!!!!!!!!!!!!!!!!!                 !         !!!!!!!!!!  
            !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!              !!     !!!!!!!!!!!!    
           !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!        !!      !!!!!!!!       
            !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!      
             !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!       
              !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  !!!!!!!!!!!!       
       !!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!        !!!!!!        
      !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!      !!!!!         
          !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!        !!!          
        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!        !          
          !!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!                       
           !!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!                         
                  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!                          
                 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!                           
                  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!                               
                  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!                               
                  !!!!!!!!!!!!!!!!!!!!!!!!!!!!                                 
                  !!!!!!!!!!!!!!!!!!!!!!!!!!                                   
                  !!!!!!!!!!!!!!!!!!!!!!!!!                                    
                   !!!!!!!!!!!!!!!!!!!!!!!!                                    
                    !!!!!!!!!!!!!!!!!!!!                                       
                    !!!!!!!!!!!!!!!!!!!                                        
                     !!!!!!!!!!!!!!!!                                          
                      !!!!!!!!!!!!!!!!                                         
                      !!!!!!!!!!!!!!!                                          
                       !!!!!!!!!!!!!!                                          
                        !!!!!!!!!!!!                                           
                        !!!!!!!!!!!!                                           
                        !!!!!!!!!!!!                                           
                          !!!!!!!!                                             
                          !!!!!!                                               
                           !!!!                                                
*/

코드 분석

이 코드는 C 언어 문법에 어긋나지 않지만 알아보기 힘들게 교묘하게 섞어 놨다. 잘 보면 위의 코드를 이해할 수 있는 몇 가지 단서를 발견할 수 있다.

1. 문자의 배열

이 코드를 잘 보면, for (b = c = 10; a = "- FIGURE?, .. 중간 생략 .. Hq!WFs XDt!"[b+++21]; )
a 변수에 대입 되는 값은 "...문자열..."[인덱스] 값인 char 문자가 된다.

b = 10 초기 값을 가지므로 배열의 인덱스 값은 [11+21], 즉 32번 문자부터 시작하므로 a 변수의 문자열 "- FIGURE?, UMKC,XYZHello Folks,\" 은 그냥 더미(쓰레기) 값임을 알 수 있다.

2. 섞어 놓은 문법 풀기

이 코드에서 for (b = c = 10; a = "- FIGURE?, .. 중간 생략 .. Hq!WFs XDt!"[b+++21]; )
배열 인덱스를 나타내는 [b+++21] 코드는 [(b++)+21] 로 풀어 쓸 수 있다.

putchar ( ++c=='Z' ? c = c/ 9:33^b&1); 코드는 일반적인 삼항식이다.
삼항식인 ++c=='Z' ? c = c/ 9:33^b&1 코드는 (++c == 'Z') ? (c = c / 9) : (33 ^ b & 1) 이렇게 풀어 쓸 수 있다.

이 두 가지의 문법적인 부분만 발견하고 풀어냈다면 다 한 것이나 다름 없다.

(++c == 'Z')는 ASCII 코드 표를 참고해 보면 (++c == 90)이 되고, c = 10인 초기 값을 갖는다. 그러므로 80번 반복 후에 c=c/9 코드의 값은 언제나 10=90/9이 되므로 10인 ASCII 코드인 'new line', 새로운 행에서 문자를 계속 찍는다.

(33 ^ b & 1) 코드는 (33 xor b and 1) 비트 연산과 같다. xor, and 연산 중 and 연산이 더 우선 순위이므로 (b and 1) xor 33 이렇게 표현할 수 있겠다.

33 - (b ^ 1) 코드로 최종적으로 표현되는데, b^1은 홀수면 1, 짝수면 0이 된다. ASCII 코드 표를 참고해 보면 33,32 값은 각각 '!' 문자와 'Space' 문자가 된다.

아래는 ASCII 코드 표 이므로 참고하기 바란다.

ASCII 코드표
ASCII 코드표

코드가 실행되는 순서대로 ASCII 값을 대입해 보면, 코드가 어떻게 실행 결과처럼 출력 되는지 이해할 수 있을 것이다.

보기 쉬운 코드로 변경

코드를 자세히 보니 별 것 아니란 걸 느꼈을 것이다. 이 코드를 리팩토링여 알아보기 쉬운 코드로 아래와 같이 바꿀 수 있다. 그럼 같은 결과를 얻을 수 있다. 혹시 잘 이해가 되지 않는다면 아래의 코드를 if 문법으로 변경해 보면 더 보기 편할 것이다.

int main()
{
    int a,b,c;

    char *str = "- FIGURE?, UMKC,XYZHello Folks,\
TFy!QJu ROo TNn(ROo)SLq SLq ULo+\
UHs UJq TNn*RPn/QPbEWS_JSWQAIJO^\
NBELPeHBFHT}TnALVlBLOFAkHFOuFETp\
HCStHAUFAgcEAelclcn^r^r\\tZvYxXy\
T|S~Pn SPm SOn TNn ULo0ULo#ULo-W\
Hq!WFs XDt!";

    for (b = c = 10; a = *(str + (b++) + 21); ) {
        for(; a-- > 64 ; ) {
            putchar ( ++c==90 ? c = c / 9 : 33 - (b&1));
        }
    }   
}
Posted by 땡초 POWERUMC

댓글을 달아 주세요

[퀴즈] 프로그래머를 위한 문제 #3

요즘 퀴즈를 풀다보니 재미가 들렸나, 필자가 문제를 하나 내보려고 한다. 어려울 수도, 그렇지 않을 수도 있는 문제이며, 효율적인 코드를 작성하는 것 보다 최대한 짧게 짜는 것이 목적이다.

문제의 유형과 정답의 유형은 지난 문제를 참고하면 된다.

미로 찾기 게임

문제는 미로 찾기 게임이다.

  1. 10 x 6 (가로, 세로) 크기에 * 문자가 채워진 직사각형
  2. 미로의 크기가 변해도 실행 가능해야 한다.
  3. 문자 S 는 입구 위치, 문자 E는 출구 위치이다.
  4. S 문자와 E 문자 사이에는 공백으로 연결된 길이 있고, 길은 여러 갈래일 수 있다.
  5. 길(공백)은 2x2(가로, 세로) 이상의 공간을 가질 수 없다.
  6. 미로 찾기를 시작하는 함수가 호출되기 이전의 초기화를 위한 코드는 코드 길이에서 제외된다.
  7. 코드 길이는 최대한 짧게 작성한다. (알고리즘 효율성은 측정 안함)
  8. 개발 언어는 무관, 언어별로 가장 짧게 작성한 코드가 우승(?)



미로 찾기 게임 예제 코드

// 아래의 변수의 초기화는 코드 길이에서 제외된다.  
map = """  
**********  
** **    E
*   * ****
* * * *  *
* *    * *
S ****   *  
**********"""

y = map.indexof("S") / maxX
x = map.indexof("S") % maxX  

// 아래 코드 부터 코드 길이를 잰다. 개행 문제는 코드 길이에서 제외  
void explore(x,y)
{
    // ...생략...
}  

정답

아래는 필자가 작성한 코드이다. 짧게 작성하는 방법은 여러 가지이므로 혹시 여러분들께서 C++ 또는 자바스크립트, C# 등으로 작성하신 코드는 댓글로 달아주세요.

Python( 파이썬 )

아래 주석의 begin code/end code 사이의 문자는 총 273 문자. 방향에 대한 가중치를 주면 훨씬 빠르게 길을 찾을 수 있지만, 여기에선 구현을 제외했다.

#123456789
m = """  
**********  
** **    E
*   * ****
* * * *  *
* *    * *
S ****   *  
**********"""

X = m.index("\n", 1) - 1
Y = m.count("\n") - 1
m = m.replace("\n", "")  

(y, x) = divmod(m.index("S"), X)  

### 총 273 바이트 ###  
########### begin code ############  
def E(x,y,a):  
 n=m[x+(y*10)]  
 if n=="*"or x<0 or x>X or y<0 or y>Y:return None

 # print "%d, %d" % (x, y)  
 if n=="E": print("END");quit()

 while a!=2 and E(x+1,y,1)!=None:pass  
 while a!=1 and E(x-1,y,2)!=None:pass  
 while a!=8 and E(x,y+1,4)!=None:pass  
 while a!=4 and E(x,y-1,8)!=None:pass  

E(x,y,0)  
############# end code ##############


## 실행 결과 ##  
0, 5  
1, 5  
1, 4  
1, 3  
1, 2  
2, 2  
3, 2  
3, 3  
3, 4  
4, 4  
5, 4  
6, 4  
6, 5  
7, 5  
8, 5  
8, 4  
8, 3  
7, 3  
5, 3  
5, 2  
5, 1  
6, 1  
7, 1  
8, 1  
9, 1  
END  


Posted by 땡초 POWERUMC

댓글을 달아 주세요

프로그래머를 위한 문제 #2

얼마 전 OKJSP 를 통해 이런 문제를 보았다.

문제는 아래의 코드 중 /* INPUT */ 주석에 알맞은 코드를 넣어, victory() 메서드가 호출되도록 완성하여라.

필자의 컴퓨터에서는 답이 (function-48)(); 로 나왔다. 

typedef int (*f)(); 
int variable = 1;   

int function() {     
   if(variable == 1 ) return 
      /* INPUT */       
   5; 
   victory();  
}   

int main() 
{     
   function();
   return 0; 
}  

[문제 코드] 위의 INPUT 주석에 알맞은 코드를 넣어라.

단 제약 조건이 있습니다.

  • 다음의 문자는 사용할 수 없음 : main, victory, asm, %, *, _, #, /, “, ‘
  • 최대 11자
  • 세미콜론(;)은 한 번만 써야 함

문제 해결 과정 #1

일단 Visual Studio 2012 C++ 로 작성한 코드인데, 이 코드는 답을 찾아가기 위한 중간 코드이다.

이 코드를 보자마자 ‘스택 프레임(Stack Frame)’을 이용해야겠다는 맘을 먹고 코드를 작성했다. Visual Studio에서 만든 C++ 프로젝트의 속성으로 들어가서 RTC 런타임 체크 기능을 꺼야 한다. 그렇지 않으면 스택을 덮어 쓸 수가 없이, AccessViolation 오류가 발생할 것이다.

프로젝트 속성 -> 구성 속성 -> C/C++ -> Code Generation -> Basic Runtime Checks -> Default 로 변경

#include "stdafx.h"  
#include <iostream>  

using namespace std;  

int victory()
{
    cout << "victory" << endl;
    return 0;
}  

typedef int (*f)();  
int variable = 1;  

int function()
{
    int a;
    int n = (int)(&a) + 8;// 일반적으로 스택은 위로 자라므로,  EBP + 4 의 위치를 구함

    // victory 메서드 시작 위치는 현재로 부터...  765임
    cout << (int)(&victory) - (int)(&function) << endl;  
    *(void**)n = (void*)((int)(&function) - 765);    

    if(variable == 1 ) return
    /* INPUT */ 
    5;
    victory();
}  

int _tmain(int argc, _TCHAR* argv[])
{
    function();
    return 0;
}  

// 실행 결과  
// -765  
// victory  

대충 그림이 나왔으니 코드를 /* INPUT */ 에 들어갈 수 있도록 예쁘게 다듬어주면 될거라고 생각했다. 그런데 VC++은 타입체크가 너무 강한지 몰라도 어떻게 예쁘게 다듬어도 오류가 난다. 줸장~ 문제와는 상관 없이 좀 더 만져봐야 겠다.

문제 해결 과정 #2

오늘 정성태 과장님이 이 문제를 보고 이야기를 나누었고 답을 보여주셨다. 답은 링크를 통해 방문하면 된다.

과장님 왈, VC++ 에서는 컴파일이 안될거라고, GCC에서 컴파일 했다고 알려주셔서 나도 얼른 GCC로 바꿨다. 궁극적으로 위의 문제 해결 과정 #2 방법을 GCC로 간결하게 표현했다. 어쨌든 정성태 과장님과 답이 거의 같다.

정성태 과장님이 아니었으면 VC++만 가지고 매달렸을텐데, 문제를 풀 수 있도록 도와주신 울 과장님께 ㄱㅅ ㄱㅅ ^^

# include <stdio.h>  

int victory()
{
    printf("victory\n");
    return 0;
}


typedef int (*f)();  
int variable = 1;  

int function()
{
    // victory 메서드가 얼마만큼 떨어져있나 찍어봄... 48만큼...
    printf("%d\n", &function - &victory);  


    if(variable == 1 ) return

        (function-48)();      /* <------- INPUT CODE */
    ;

    5;

    victory();

}  

int main(int argc, const char * argv[])
{
    printf("---------------\n");
    function();
    return 0;
}   

// 실행 결과  
48
victory  


Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. orbit 2016.12.20 14:16 Address Modify/Delete Reply

    재미있는 글이네요
    저는 VS2015에서 이렇게 풀었습니다.

    #include "stdafx.h"
    #include "victory.h"

    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif

    #include <iostream>

    int victory()
    {
    std::cout << "victory" << std::endl;

    return 0;
    }

    typedef int(*f)();
    int variable = 1;

    int function() {

    f foo = (f)((int)function - ((int)function - (int)victory));

    if (variable == 1) return
    /* INPUT */ foo();
    5;
    victory();
    }

    int main()
    {
    function();
    return 0;
    }