2010년 10월 22일 단상들

단상들 2010. 10. 23. 00:33 posted by whiterock
  • 예전에 읽다가 만 책을 다시 본다. 실용적인 철학 책처럼 느껴진다. ㅎㅎㅎ “가치, 원칙, 실천방법”에 대한 이야기.(me2book 익스트림 프로그래밍) 2010-10-22 13:48:05
    익스트림 프로그래밍
    익스트림 프로그래밍

이 글은 whiterock님의 2010년 10월 22일의 미투데이 내용입니다.

2010년 9월 16일 단상들

단상들 2010. 9. 17. 00:31 posted by whiterock
  • 날씨 좋은 날에 한강을 건너는 것은 기분을 개운하게 만든다.(출근 날씨 me2mobile me2photo) 2010-09-16 09:18:52

    me2photo

  • 감기약 왜이리 독하냐. 정신 못차리겠네..ㅡ,.ㅡ;;;(약) 2010-09-16 14:28:30
  • 회사에 있길래 1시간 정도 투자해서 훑어 봄. 컴퓨터를 잘 모르던 초딩 때 베이직 배우던 느낌이 들었다. 취미로 프로그래밍을 배우고자 하시는 분들에게 건네주면 좋은 가이드가 되지 않을까 생각이 든다.(me2book 개념을 잡아주는 프로그래밍 정석) 2010-09-16 18:22:07
    개념을 잡아주는 프로그래밍 정석
    개념을 잡아주는 프로그래밍 정석

이 글은 whiterock님의 2010년 9월 16일의 미투데이 내용입니다.

C에서 실수를 줄이는 코딩 스타일

프로그래밍 2010. 1. 14. 22:42 posted by whiterock
프로그램의 소스 코드는 사람이 작성을 한다. 다른 작업들도 그렇겠지만, 사람들은 상황에 따라서 실수들을 종종한다. 따라서 실수를 가능한 하지 않게 여러가지 방법들을 이용하여 보완을 하는 게 현실이다. 경험적으로 C 언어를 종종 사소한 실수를 많이 했던 것들에 대해서 보완하는 방법 중 한가지가 코딩 스타일이다. 그 중 몇 가지를 정리를 해본다.

조건절 비교

조건절에서 상수 값과 변수의 값이 일치하는지 비교할 때, 대부분의 예제 코드들 보면 이런 스타일로 나온다.

[code c] if (a == 1) { a++; } [/code]


간혹 실수로 "==" 대신 "="를 쓰는 경우가 있다. 컴파일 옵션으로 경고 수준을 높이지 않으면 경고로도 나오지 않는다. 경고 수준을 높이고 가능한 모든 경고는 제거하는 습관은 말 안해도 기본이다.

이 경우는 다음과 같은 스타일로 코딩을 하면 실수를 문법 에러로 잡아낼 수 있다.

[code c] if (1 == a) { } [/code]

if .. else .. 문

디버깅을 하다 보면 심심치 않게 실수를 하게 되는 부분이 if ... else ... 관련된 부분이다. C언어 메뉴얼에서도 if else 관련 주의 사항으로 {} 없는 경우에 대한 얘기가 나온다. 수 많은 코드를 눈으로 그냥 훑고 지나가다 보면 모양만 유심히 보다가 놓치게 되는 경우가 많다. 다음 경우 같은 경우가 되겠다.

[code c] if (1 == a) a++; a++; [/code]

그리고, 디버깅하다가 간혹 보게되는 실수인 경우가 조건을 만족하지 않았을 때, 해줘야 할 것들을 빠뜨리는 경우가 있는 것을 보게 된다. 그래서 거짓일 때 처리할 게 없다는 것을 명확히 표시를 해 주는 것도 실수를 줄이는 데 도움이 된다.

그래서 if 문 관련 코딩 스타일로 다음 형태로 명확하게 블럭으로 묶어 주고, 처리할 게 없다는 것을 명확하게 표시를 해준다.

[code c] if (1 == a) { a++; } else { /* do nothing */ } [/code]

Return 값 처리 무시 표시

라이브러리들의 함수들을 보면 대부분 처리 결과에 따라 Return 값을 넘겨준다. 이런 결과들을 제대로 처리를 해주는 것 또한 튼튼한 프로그램을 만드는 것에 대한 기본이다. 그러나 때로는 실행에 별다른 영향이 없어서 이 Return 값을 무시할 수 도 있다. 그래서 의도적으로 무시를 한다는 표시로 (void)를 함수 앞에 붙여준다.

[code c] (void)close(fd); [/code]

( )를 이용하여 명확한 연산자 우선 순위 표시

10년 넘게 C 언어를 사용해 왔지만, 아직도 연산자 우선 순위가 아리까리 하다. 간혹 이런 연산자 우선 순위를 잘 못 알고 있어, 디버깅 하다 보면 의도하지 않게 동작하는 코드들을 종종 만나기도 한다. 그래서 가장 높은 우선 순위를 가진 ( )를 이용하여 명확하게 한다.

[code c] a = (a + (a * 2)) / 2; [/code]


그 외에도 더 있겠지만, 지금 생각나는 것들만 정리를 해보았다. 핵심은 명확하게 표현을 하는 것이다.
"C 함수에서 2개의 return 문을 사용하게 제약한 코딩 스타일" 얘기한 함수 구조를 갖추기 위해서 예외처리 관련 매크로를 사용하고 있다.

이 예외처리 매크로는 goto 문을 이용하여 만들어 졌다. C로 구현된 매크로들을 Google에서 검색해보면 setjmp(), longjmp() 같은 것을 이용한 것들도 보이나, C 언어에서 제공되는 goto 만으로 만들어 사용하는 것이 이해하기 쉽고, 다양한 환경에서 무난하게 돌아가는 장점이 있다.

회사에서 사용하는 것보다 좀더 단순화시켜서 비슷하게 만들어 보겠다. C++이나 Java에서 사용하는 try { } catch { } 같은 스타일은 아니고, 크게 예외 판단 매크로들, 예외를 받는 매크로들로 구성이 된다.

직접 보는게 제일 이해가 빠를 것이다. 다음 프로그램은 "hello.txt"라는 파일에 "hello world"를 출력하는 프로그램이다.
[code c]/* * http://blog2.thewhiterock.net/ */ #include <stdio.h> #include <errno.h> #include "exception.h" int main(int argc, char **argv) { FILE *fp = NULL; int result = 0; fp = fopen("hello.txt", "w"); EXCEPTION_TEST_RAISE(fp == NULL, LABEL_OPEN_FAIL); result = fprintf(fp, "hello world\n"); EXCEPTION_TEST_RAISE(result < 0, LABEL_WRITE_FAIL); result = fclose(fp); EXCEPTION_TEST_RAISE(result != 0, LABEL_CLOSE_FAIL); return 0; EXCEPTION(LABEL_OPEN_FAIL) { (void)printf("Fail to open file 'hello.txt' ( %d )\n", errno); } EXCEPTION(LABEL_WRITE_FAIL) { (void)printf("Fail to write file 'hello.txt' ( %d )\n", errno); } EXCEPTION(LABEL_CLOSE_FAIL) { (void)printf("Fail to close file 'hello.txt' ( %d )\n", errno); } EXCEPTION_END; return -1; }[/code]

예외를 검사하는 EXCEPTION_TEST_RAISE() 매크로와 예외를 받는 EXCEPTION(), EXCEPTION_END 매크로들이 보인다. main() 함수의 구조를 보면 상단에는 주요 로직이 위치해 있고, 하단에는 예외 처리 로직이 위치한 형태를 가진다.

이 매크들이 구현된 것 또한 소스 코드를 직접 보는 것이 이해가 빠를 것이다. 다음은 exception.h 소스 이다.
[code c]/* * http://blog2.thewhiterock.net/ */ #ifndef __EXCEPTION_H__ #define __EXCEPTION_H__ #define EXCEPTION(label_name) \ goto EXCEPTION_END_LABEL; \ label_name : #define EXCEPTION_END \ EXCEPTION_END_LABEL: \ do \ { \ } while (0) #define EXCEPTION_TEST(expression) \ do \ { \ if (expression) \ { \ goto EXCEPTION_END_LABEL; \ } \ else \ { \ } \ } while (0) #define EXCEPTION_TEST_RAISE(expression, label_name) \ do \ { \ if (expression) \ { \ goto label_name; \ } \ else \ { \ } \ } while(0) #define EXCEPTION_RAISE(label_name) \ do \ { \ goto label_name; \ } while(0) #endif /* __EXCEPTION_H__ */[/code]

지금 회사에서는 함수 안에서 return 문을 딱 2개만 쓸 수 있게 코딩 스타일로제한을 했다. 그래서 대부분 구현된 함수 구조를 보면 다음과 같다.

function( ) 
{ 
    /* 로직 구현 부분 */ 

    return 성공을 나타내는 코드; 

    /* 예외 처리 부분 */ 

    return 에러를 나타내는 코드; 
}

이런 형태로 제약을 하면서 실행 흐름을 단순하게 하여 가독성을 높이고, 버그를 줄이는 효과가 있다.

이런 형태로 구현될 수 있게 도와주는 예외 처리를 위한 매크로들이 있는데, 자세한 내용은 다른 포스트로 정리를 하겠다. 예외 상황 조건을 충족하면 예외 처리 부분으로 넘어가게 goto 문을 이용한 매크로라는 것만 알면 된다.
시스템 소프트웨어를 개발하다 보면 프로그램이 어떻게 수행이 되는지 궁금할 때가 많다. Ulich Drepper라는 개발자의 홈페이지에 이런 궁금증을 해결해 주는 자료가 많다. 이 아저씨에 대한 간단한 이력을 보면 Redhat에서 일을 하고 있으며, GNU C Library의 핵심 개발자이다.

http://people.redhat.com/~drepper/ <- 바로 여기가 이 아저씨 홈페이지

예전에 회사에서 이 아저씨가 작성한 2개의 문서를 가지고 세미나를 진행한 적이 있었다. "What Every Programmer Should Know About Memory""How to write shared libraries"이다. 특히 메모리 관련된 문서를 보면서 세상에 이런 사람도 있구나 하는 놀라움을 느꼈다. 하드웨어부터 소프트웨어까지 메모리를 효율적으로 사용하는 프로그래밍에 관련된 내용이 포함되어 있는데, 어떻게 이 모든 것을 알고 있나 싶을 정도였다. 이런 생각을 나 혼자 한 것이 아닌, 세미나를 같이 진행하는 모두가 동의하였다.

홈페이지에 있는 자료를 보면, GNU C Library 핵심 개발자답게 glibc와 관련된 다양한 기술 정보들이 가득하다. Linux 또는 UNIX 기반에서 시스템 프로그래밍을 하고자 한다면 여기에 있는 문서들을 한번은 봐야할 필요가 있지 않나 싶다.

이 아저씨의 자료들에서 중요하다 싶은 것들을 정리하는 것만으로도 이 블로그의 내용이 풍성해지지 않을까 싶다. ㅎㅎㅎ