2011. 9. 14. 17:38

[ 051 ] - 구조체 변수를 만들면 반드시 초기화하자.

구조체 변수를 만들면 그 변수가 포인터이든지 아니면 구조체 변수이든지 상관없이 반드시 초기화를 해주어야 합니다.

특히 고수들은 초기화 작업을 위해 구조체를 만들고 초기화하는 별도의 함수를 사용합니다.

이 장의 본문에서 예로 든 생성 및 초기화 함수의 코드를 실무에서 구조체를 사용하는

프로그램을 작성할 때 적용할 수 있도록 충분히 연습하세요.

 

[ 052 ] - 구조체 내부에서 포인터를 사용하면 내부 항목도 메모리를 할당해 주자.

구조체를 사용할 때 내부 항목으로 char *형을 사용하는 경우가 가끔 있습니다.

반드시 char *형이 아니더라도 포인터형을 내부 항목으로 사용하는 경우가 있죠.

이와 같은 경우에는 구조체의 내부 항목도 메모리 할당을 해주어야 합니다.

만약 내부 항목이 포인터임에도 메모리 할당을 하지 않으면 프로그램을

실행하는 중에 이 포인터를 사용할 때 오류가 생길 수 있습니다.

 

[ 053 ] - 구조체 포인터를 해제할 때는 철저하게 하자.

구조체 포인터로 할당받은 변수를 사용하고 나서 해제할 때는 반드시 해제하는 순서를 지켜야 합니다.

해제하는 순서는 구조체 내부 항목으로 할당받은 메모리 영역부터 해제한 후에 그 작업이 모두 끝나면 실제

구조체 포인터도 해제해 주는 것입니다. 특히 구조체 포인터의 메모리를 해제했다고 해서 해야 할 일이 모두

끝난 것이 아니라 반드시 구조체 포인터 자체를 NULL로 초기화해야 합니다. 구조체 포인터도 또한

포인터이기 때문에 NULL로 초기화하지 않고 사용하면 치명적인 메모리 에러가 일어날 수 있습니다.

 

[ 054 ] - 구조체 배열을 잘 사용하면 데이터베이스가 부럽지 않다.

데이터베이스는 수많은 데이터를 특별한 규칙으로 분류하여 체계적으로 저장해 놓고

사용자가 원할 때마다 꺼내어 빠르게 출력하는 것입니다. 이 장에서 다룬 구조체 배열을 사용하면

데이터베이스와 똑같은 기능을 낼 수는 없지만 이와 비슷한 기능을 흉내 낼 수는 있습니다.

물론 구조체 포인터를 이용해서 같은 기능의 작업을 할 수도 있지만 인덱스로 직접 메모리를 접근할 수 있다는

배열의 장점과 여러 가지 자료형을 하나로 묶는다는 구조체의 장점을 모두 살릴 수 있는 방법은

구조체 배열이 유일하기 때문에 익혀 두면 여러모로 유용합니다.

 

[ 055 ] - 구조체 안에 구조체를 선언하자.

구조체 안에 구조체를 선언하는 방법은 객체 지향 프로그래밍의 상속(Inheritance)과 비슷합니다.

객체 지향에서 상속은 부모 클래스에서 선언한 멤버 변수나 멤버 함수를 상속받아서 자식 클래스에서

그대로 사용할 수 있는 것입니다. 구조체 안의 구조체는 하나의 구조체 안에서 기존의 구조체를

하나의 항목으로 선언할 수 있습니다.

따라서 구조체 안의 구초제는 마치 레고 블록으로 건물을 짓듯이 기존의 구조체로 선언한 자료형을

사용하여 손쉽게 새로운 자료형을 만들 수 있다는 장점이 있습니다.

 

[ 056 ] - 배열과 구조체 포인터를 연결해 사용하는 고수들의 개발 노하우를 배우자.

본문에서 다룬 배열과 구조체 포인터를 연결하는 방법은 일반적으로 많이 알려진 방법은 아니지만

문자형 배열과 구조체 포인터를 연결하면 메로리를 공유할 수 있다는 장점이 있습니다.

다음 장의 공용체를 사용하는 부분에서도 다루지만 배열과 구조체 포인터를 연결하는 방법은

공용체를 사용하는 방법보다 훨씬 강력한 기능을 하는 기술입니다.

한 번에 잘 이해가 안 되면 몇 번이고 배운 내용을 반복하면서 완전하게 자기 것으로 만들기 바랍니다.

 

[ 057 ] - 공용체를 사용하자.

공용체를 구조체와 혼동하면 절대 안 됩니다. 구조체는 여러가지 자료형을 그룹으로 묶는 역할을 하는 반면,

공용체는 하나의 메모리 공간을 여러 가지 자료형이 공동으로 사용하는 역할을 합니다.

따라서 공용체를 사용하면 하나의 메모리 공간을 두 개 이상의 자료형이 공유할 수 있기 때문에

하나의 데이터를 여러 가지 자료형에 맞게 사용할 수 있는 장점이 있습니다.

 

[ 058 ] - 공용체와 구조체를 함께 사용하자.

공용체와 구조체를 함께 사용하는 방법은 임베디드 프로그래밍 분야에서는 기본적인 노하우입니다.

특히 구조체를 사용하여 비트 단위로 각 항목을 설정하고 공용체로 바이트 단위로 접근하면

비트 처리와 바이트 처리를 모두 할 수 있기 때문에 임베디드처럼 비트 처리와 바이트 처리가

모두 필요한 프로그래밍 분야에서 필수라고 할 수 있습니다.

 

[ 059 ] - 고수들이 사용하는 전처리기의 기능을 알아 두자.

전처리기는 컴파일 과정 전에 일부 과정을 처리하기 위해서 실행되는 일종의 애벌빨래와 같은 기능입니다.

전처리 과정에서 처리하는 내용은 다음과 같습니다.

 

① #define

필요한 표현을 미리 정의해 둘 수 있습니다.

② #pragma

하나의 헤더 파일을 여러 소스 파일에서 사용하는 경우에 일어날 수 있는 중복으로 인한 링크 오류를 막습니다.

③ #ifdef(또는 #ifndef) ~ #else ~ #endif

컴파일과 실행되는 코드들을 그룹으로 나눌 수 있습니다. 주로 디버깅용과 릴리스용으로 나눌 때 사용합니다.

④ 매크로 함수

작은 연산을 하는 경우라면 일반 함수 대신 #define 문을 사용하여 매크로 함수를 만들 수 있습니다.

 

[ 060 ] - Make를 알아 두자.

Make는 흔히 리눅스나 유닉스에서만 사용하는 도구로 알고 있는데,

유닉스나 리눅스에서 Make 도구를 자주 사용하는 것은 맞지만

그렇다고 윈도우 프로그램에서 사용하지 못하는 것은 아닙니다.

단지 윈도우 프로그래밍을 할 때는 비주얼 C++나 비주얼 베이직이라는 통합 개발 환경에서

전용으로 사용하는 도구가 있기 때문에 굳이 Make 도구를 사용할 필요가 없는 것이죠.

비주얼 C++의 전신인 마이크로소프트 C 7.0만 하더라도 Make 도구를 사용했습니다.

리눅스나 유닉스 프로그래밍을 하려면 반드시 Make 도구와 Makefile을 작성하는 방법을 알고 있어야 합니다.

 

[ 061 ] 디버깅을 잘하는 사람이 진짜 고수다.

아무리 프로그래밍 실력이 좋은 개발자라고 해도 프로그래밍을 하면서 디버깅을 하지 않는 경우는 없습니다.

처음 프로그램을 설계할 때부터 완벽하게 설계하고 프로그래밍을 하는 사람일지라도 결국 오류가 생기게 마련입니다.

어떤 언어이든지 상관없이 디버깅을 얼마나 효율적으로 빨리 할 수 있느냐는

개발자가 어느 정도 레벨의 프로그래머인지 판단하는 데 중요한 요소입니다.

지금까지 이 책에서 다룬 수많은 프로그래밍 노하우 가운데 최고는 디버깅입니다.

디버깅을 잘하는 사람이야말로 최고의 고수라고 할 수 있기 때문에 효율적으로

디버깅을 할 수 있도록 꾸준하게 연습하기 바랍니다.

Posted by devanix
2011. 9. 14. 17:25

[ 041 ] - #define 문을 사용하여 소스 코드에서 사용하는 상수들을 미리 정의하자.

C 언어에서 사용하는 #define 문은 사무실에 놓여 있는 꽃병이나 화분처럼

그저 보는 데 만족하라고 있는 기능이 아닙니다.

고수 프로그래머들의 소스 코드를 보면 여러 가지 #define 문을 사용하여 굳이 소스 코드에 대한 주석을 보지 않고도

이 소스 코드의 기능이 무엇인지 알 수 있을 정도입니다. 대부분의 초보 프로그래머들이 귀찮거나 번거롭다고 해서

#define 문을 사용을 주저하는 경우가 많은데 #define 문을 사용하지 않고 0과 1로 도배하다시피

한 소스 코드는 한두 달 지나면 소스 코드를 만든 프로그래머조차 쉽게 이해하지 못할 만큼 어려운 코드가 됩니다.

 

[ 042 ] - 포인터는 선언하자마자 초기화하자.

포인터는 C 언어에서 최고로 강력한 기능입니다. 하지만 이와 같이 강력한 기능을 제대로 사용하지 않으면

여러 가지로 어려운 일이 생길 수 있으므로 일단 포인터를 사용하면 반드시 초기화하는 습관을 갖는 것이 좋습니다.

 

[ 043 ] - 논리적인 버그가 생길 수 있는 부분에 검사 코드를 넣어 두자.

초보 프로그래머들이 오해하는 것 가운데 하나는 컴파일러가 만능이라고 착각하는 것입니다.

컴파일러에서 특별한 에러나 경고 표시가 없으면 그 프로그램은 전혀 문제가 없을 것이라고 생각하지만

프로그램은 그것을 개발한 프로그래머의 의도대로만 실행되지는 않습니다.

대부분의 프로그래밍의 고수들은 말도 안 되는 상황을 많이 경험했기 때문에 고수들은 혹시라도 문제가

생길 수 있는 부분을 철저하게 검사하도록 코드를 구현합니다.

물론 너무 많은 검사 코드가 프로그램의 원래 기능을 방해해서는 안 되겠지만,

만에 하나 일어날 수도 있는 버그를 미리 막을 수 있도록 프로그램에 검사 코드를 추가하는 습관을 들이는 게 중요합니다.

 

[ 044 ] - 메모리의 할당과 해제를 할 때에는 별도의 함수를 사용하자.

C 언어가 메모리에 너그러운 프로그래밍 언어이기 때문에 프로그램 안에서 메모리를 사용할 때도

malloc() 함수와 free() 함수만 사용해 주면 특별한 문제는 없습니다.

하지만 malloc() 함수와 free() 함수만 사용하기에는 C 언어의 지나친 너그러움이 오히려 장애가 될 수 있습니다.

대부분의 프로그래밍 고수들은 malloc() 함수와 free() 함수를 직접 불러서 사용하지 않고 별도의 메모리 할당과 해제를

위한 함수를 만들어서 사용합니다. 이와 같은 함수를 래퍼 함수(wrapper function)라고 하는데 래퍼 함수를 사용하면

메모리를 사용할 때 생길 수도 있는 버그를 대부분 미리 막을 수 있습니다.

본문에서 다룬 malloc() 함수와 free() 함수에 대한 래퍼 함수들을 잘 살펴보고

여러분만의 래퍼 함수를 만드는 습관을 들이세요.

 

[ 045 ] - 하나 이상의 반환 값이 필요할 때는 포인터를 사용하자.

지금까지 C 언어에서 함수는 오직 하나의 반환 값만을 처리할 수 있다고 귀에 못이 박히도록 배웠을 것입니다.

하지만 실무에서 프로그래밍을 하다 보면 두 개 이상의 반환 값이 필요한 경우가 있습니다.

특히 함수의 정상적인 실행 여부를 확인하기 위해서는 반환 값이 필수인데, 바로 이때 포인터를 매개변수로 사용하면

실제 반환 값처럼 사용할 수 있습니다. 이와 같은 방법은 고수들이 자주 사용하는 방법 가운데 하나이며

프로그램의 버그를 막기에도 더할 나위 없이 좋은 프로그래밍 습관입니다.

 

[ 046 ] - 포인터가 가리키는 값을 증가시킬 때에는 반드시 괄호를 사용하자.

종종 어떤 프로그래머들은 포인터가 가리키는 값을 증가시키는 코드를 다음과 같이 만듭니다.

*pNum++;

하지만 위의 코드를 실행해 보면 포인터가 가리키는 데이터의 값이 1씩 증가하는 것이 아니라

포인터 자체의 주소 값이 1씩 증가하는 것을 알 수 있습니다.

사실 이와 같은 버그는 찾기도 어렵지만 프로그래머가 예상하지 못한 곳으로 포인터가 이동하기 때문에

나중에 심각한 문제를 일으킬 수 있습니다.

따라서 포인터가 가리키는 값을 증가시키기 위해서 반드시 괄호를 사용해야 합니다.

(*pNum)++;

괄호를 사용하는 것 하나만으로도 최소한 포인터를 잘못 사용해서 일어나는 문제의 대부분을 줄일 수 있습니다.

 

 

[ 047 ] - malloc()과 free() 함수를 정확하게 구분해서 사용하자.

프로그램에서 흔히 지나치기 쉬운 기능 가운데 하나가 malloc()과 free() 함수입니다.

malloc() 함수는 동적으로 메모리를 할당하며 반대로 free() 함수는 할당받은 메모리를 해제하는 역할을 합니다.

이 장에서는 malloc()과 free() 함수를 사용할 때 일어날 수 있는 몇 가지 문제들을 살펴봤습니다.

여기에서 다룬 문제는 예상되는 문제들 가운데 간단한 것들이며 그 밖에도 많은 문제들이 도사리고 있습니다.

malloc()과 free()를 사용할 때 일어나는문제들을 막기 위해서는 malloc()과 free() 함수를 사용할 때마다

주의 깊게 살펴봐야 하며 그러한 문제들을 예상해서 미리 그에 대한 대비 코드를 작성해두어야 합니다.

 

[ 048 ] - 모든 자료형을 복사할 때는 memcpy() 함수를 사용하자.

초보 프로그래머들이 가장 많이 사용하는 복사 함수는 strcpy() 함수입니다.

strcpy() 함수는 문자열을 복사하기 위해 만들어진 함수이기 때문에 구조체나 다른 자료형을 복사하기는 어렵죠.

이와 같은 경우에 대부분의 초보 프로그래머들은 특정한 자료형을 복사하기 위해

자신이 직접 함수를 만들어서 사용하곤 합니다. 하지만 이때 요긴하게 사용할 수 있는 함수가 memcpy()입니다.

한 번만 memcpy() 함수의 사용법을 익혀 두면 프로그래밍을 할 때 의외로 편할 때가 많으므로 꼭 기억해 두세요.

 

[ 049 ] - 초기화를 할 때는 memset() 함수를 사용하자.

어떤 데이터든지 만든 후에는 초기화하는 습관을 갖는 것이 좋습니다.

하지만 천차만별인 자료형에 맞게 초기화를 하기란 쉽지 않은 일이죠.

이와 같은 경우에 memset() 함수를 사용하면 쉽게 초기화를 할 수 있습니다.

특히 초보 프로그래머들은 배열을 초기화할 때 for 문을 사용하는 경우가 많은데

이때야말로 memset() 함수를 사용하는 것이 좋습니다.

 

[ 050 ] - 함수 포인터를 사용하자.

함수 포인터를 사용하면 프로그램의 유지 보수가 한결 간편해집니다.

이와 같은 함수 포인터의 장점 때문에 커널이나 임베디드 프로그램과 같이

최적화와 유지 보수성이 좋아야 하는 프로그램 코드에서는 함수 포인터를 자주 사용합니다.

하지만 함수 포인터를 정확하게 이해하지 못하면 코드를 이해할 수 없을뿐더러 수정하기가 여간 어렵지 않으므로

필요한 때 함수 포인터를 사용할 수 있도록 개념을 이해하고 사용 방법을 익혀 두세요.

Posted by devanix
2011. 9. 14. 17:10

[ 031 ] - 여러개의 상수를 선언할 때 #define보다 열거형을 사용하자.

우리는 #define 문의 사용에 익숙하기 때문에 보통 상수를 선언할 때 #define 문을 사용하는 경우가 많습니다.

한두 개의 상수를 선언하는 경우라면 #define 문을 사용해도 되지만 여러 개의 상수를 순차적으로 정의할 필요가

있을 때에는 #define 문을 사용하는 것보다 열거형을 사용하는 것이 좋습니다.

#define KOREAN 10

#define ENGLISH 11

#define MATH 12

#define HISTORY 13

위와 같이 과목에 따라 과목의 코드를 #define 문을 사용하여 상수로 처리하면

열거형을 사용할 때 좀더 간단하게 표현할 수 있습니다.

enum {

    KOREAN = 10,

    ENGLISH,

    MATH,

    HISTORY

};

 

특히 열거형을 사용할 때의 장점은 단순히 코드를 작성할 때의 시간과 수고를 덜어준다는 것뿐만 아니라

열거형을 사용해서 상수를 선언하면 서로 다른 이름의 상수를 같은 값으로 정의할 수 있는 오류를 막을 수 있습니다.

 

[ 032 ] - 비트 연산을 할 때는 자료형의 크기를 고려하자.

비트 연산을 사용할 때 가장 주의해야 할 것은 현재 사용하는 자료형의 크기입니다.

문자형은 1바이트의 자료형, 정수형은 4바이트의 자료형이라는 것을 잊으면 안 됩니다.

특히 초보 프로그래머들이 아무 생각없이 사용하는 정수형은 4바이트의 크기를 갖기 때문에 비트 연산을 사용할 때도

그 크기를 반드시 고려해서 프로그래밍해야 합니다.

 

[ 033 ] - ~ 연산자와 ! 연산자의 차이를 확실하게 알아 두자.

NOT 연산자는 ! 연산자와 ~ 연산자의 두 가지 종류가 있습니다.

이 두 연산자는 비슷한 기능을 하는 것처럼 보여도 실제로 적용한 결과는 아주 다른 것을 알 수 있습니다.

! 연산자는 논리 연산자이므로 0 과 1만 나타내기 때문에 주로 제어문이나 조건문에서 사용하고,

~ 연산자는 다른 비트 연산자들과 함께 사용하며 0비트의 경우에는 1비트, 1비트인 경우에는 0비트로 비트 변환을

하는 연산자라는 기능을 정확하게 이해해야 합니다.

연산자를 잘못 사용해서 생기는 문제는 디버깅하기가 무척 까다롭습니다.

소스 코드의 길이가 수천, 수만 줄의 분량이라면 찾다가 포기하는 경우가 부지기수입니다.

저도 연산자 하나를 잘못 사용해서 하룻밤을 꼬박 지새고 모니터의 목을 조르며 흥분한 기억이 납니다.

사실 아무것도 아닌 것 같이 보이는 간단한 기능의 차이를 아는 것이 고수와 초보의 차이라는 것을 잊지 마세요.

 

[ 034 ] - 모든 변수는 메모리에 할당된다는 사실을 기억하자.

초보 프로그래머들이 프로그래밍을 하다 착각하기 쉬운 것 가운데 하나는 프로그램의 코드에서 사용한

변수들이 메모리와 상관없이 사용된다는 것을 잊는 것입니다.

예를 들면 int num;이라고 선언한 경우 이 변수 num을 프로그래머는 알파벳 문자 num으로 생각하지만

실제 컴퓨터에서는 'num'이라는 이름을 갖는 정수형 크기인 4바이트 공간으로 인식합니다.

물론 컴퓨터에서도 'num'이라는 이름을 알기 때문에 num이라는 변수를 두 개 이상 중복하여 사용할 수 없습니다.

하지만 num = 15;와 같이 변수 num에 어떤 값을 대입하는 과정을 보면 변수 num에 할당된 4바이트 메모리 공간 안에

15라는 값을 복사하는 과정으로 처리됩니다. 이와 같이 모든 데이터가 메모리에 할당된다는 것이

C 프로그래밍 언어에서 포인터를 사용할 수 있는 근본 취지인 것입니다.

따라서 변수 하나 하나가 각각의 메모리 공간을 가지며 그 메모리 공간은 포인터를 사용해서

접근할 수 있다는 점을 꼭 기억하고 프로그래밍 하세요.

 

[ 035 ] - 10진수 표현보다 2진수나 16진수에 더 익숙해지자.

컴퓨터를 공부하다 보면 반드시 2진법에 대해서 배웁니다. 컴퓨터 자체가 0과 1 두 가지 값만을

사용하므로 문자나 숫자, 부동소수점에 상관없이 오직 0과 1로 나타내기 때문에

컴퓨터를 사용할 때 2진법에 대한 이해는 필수입니다.

컴퓨터에서 0과 1을 사용하여 숫자를 표현하는 방법은 간단합니다.

다음은 3개의 비트를 사용해서 0부터 7까지의 10진수를 표시한 예입니다.

000 → 0

001 → 1

010 → 2

011 → 3

100 → 4

101 → 5

110 → 6

111 → 7

이와 같은 방법을 이용해서 컴퓨터는 숫자와 문자를 자유롭게 표현할 수 있습니다.

하지만 인간은 컴퓨터와 달리 10진수의 표현 방식에 더 익숙합니다. 초보 프로그래머들은 주로 10진수를 사용하지만

경험이 좀더 많은 프로그래머들은 10진수보다 2진수나 16진수를 이용합니다.

특히 2진수보다 16진수를 더 잘 이용하는데 이렇게 16진수를 이용하는 이유는 컴퓨터 내부에서

모든 데이터는 무조건 2진수로 표현되지만 2진수를 직접 사용하기에 코드의 길이가 너무 길기 때문에 2진수를

좀더 짧게 표현할 수 있는 16진수를 이용하는 것입니다.

실무에서 프로그래밍을 할 때에는 11, 12, 13 … 과 같은 10진수보다 A, B, C … 와 같은

16진수 표현법을 익혀 두는 것이 코드를 만들거나 다른 고수들의 코드를 분석할 때에도 도움이 될 것입니다.

 

[ 036 ] - 메모리의 주소 개념에 대해서 제대로 알고 프로그래밍하자.

메모리를 사용할 때는 메모리의 주소를 이용하여 접근합니다.

이와 같은 규칙은 '서울시 ○○구 △△동 ●●번지'의 주소 개념과 비슷합니다.

메모리도 각각의 주소를 나누어서 사용하며 C 언어에서는 메모리의 주소를 포인터로 처리할 수 있습니다.

모든 데이터가 메모리에 저장되어 있기 때문에 데이터를 사용할 때에도 주소를 이용하며,

프로그램에서 사용하는 모든 변수, 함수 등도 주소로 접근합니다.

예를 들면 다음과 같은 코드가 있습니다.

int num;

num = 15;

 

위의 코드는 변수 num 안에 15라는 값을 대입하는 내용입니다.

위의 코드는 겉으로 보기에 주소를 전혀 사용하지 않는 것 같지만 컴퓨터는 위의 코드를 주소로 바꾸어서 사용합니다.

mv 0x4727ac, $15

 

위의 어셈블리어 코드는 num = 15;라는 코드가 실제 컴파일된 후의 모습을 나타냅니다.

컴퓨터의 입장에서는 num이라는 변수는 전혀 사용하지 않으며

오직 변수 num에 대한 0x4727ac라는 주소 값을 이용합니다.

초보 프로그래머들 가운데에는 메모리 공간에 변수 num이라는 이름을 가진 구역이 생기고,

그 안에 15라는 값을 넣는다고 오해하는 경우가 있는데 모든 변수는 주소를 이용해서 연산한다는 것을 꼭 기억하세요.

 

또 프로그램을 만들다 보면 포인터의 주소나 변수의 주소값을 출력하는 다음과 같은 코드를 사용하는 경우가 있습니다.

printf("Addr of data : %#x\n", data);

 

위의 결과가 다음과 같을 때 위의 변수 data의 주소 값 0x4727ac가

실제 메모리의 물리적인 주소를 의미하는지 생각해 봅시다.

Addr of data : 0x4727ac

 

대부분의 초보 프로그래머들은 이 주소가 실제 메모리의 물리적인 주소라고 생각하겠지만,

운영체제가 없는 임베디드 프로그램과 같이 특수하게 절대 주소를 사용하는 분야가 아니라

보통 일반적으로 사용하는 PC나 워크스테이션과 같은 컴퓨터에서 출력하는 주소는 절대 주소가 아닙니다.

예를 들면 제 주소가 '서울시 광진구 자양3동 123-45번지'라고 할 때, 이 주소는 절대 주소가 아니라 상대 주소입니다.

절대 주소라면 [위도 : 123° 45′ 67″, 경도 : 123° 45′ 67″]과 같이 표현해야 할 것입니다.

마찬가지로 위의 경우와 같이 컴퓨터에서 출력하는 주소도 절대 주소가 아니라 상대 주소라는 것을 이해해야 합니다.

따라서 메모리의 주소라고 해서 실제 물리적인 메모리의 주소라고 생각하면 안 된다는 사실을 기억하기 바랍니다.

 

[ 037 ] - 문자열을 다루는 세 가지 방법을 알아 두자.

C 언어에서는 세 가지 방법을 사용하여 문자열 데이터를 저장합니다.

이 가운데 두 가지 방법은 배열을 이용하는 것이고 마지막 하나는 문자형 포인터를 이용하는 방법입니다.

char buf[5] = {'a', 'b', 'c', 'd', 'e'};

char buf[5] = "abcde";

char *ptr = "abcde";

대부분의 프로그래머가 사용하는 방법은 세 번째 문자형 포인터를 이용하는 방법입니다.

 

[ 038 ] - 문자열의 끝에는 반드시 '\0' 표시를 하자.

문자열을 사용할 때에는 문자열의 끝에 '\0' 표시를 해주어야 합니다.

char *ptr = "abcde";와 같이 문자형 포인터를 사용하는 경우라면 자동으로 문자열의 끝에 '\0' 기호가 추가되지만

그렇지 않다면 항상 프로그래머가 추가해주어야 합니다.

특히 문자열을 복사하기 위해서 malloc()으로 새로운 문자열을 메모리에 할당하려면

다음과 같이 문자열의 크기에 1을 더해서 '\0' 문자도 복사해주어야 합니다.

dstptr = (char *)malloc(strlen(srcptr) + 1);

 

 

[ 039 ] - malloc() 함수를 사용할 때는 sizeof() 함수와 strlen() 함수를 구별해서 사용하자.

간혹 프로그래머들 가운데 sizeof() 함수와 strlen() 함수의 사용을 혼동하는 사람들이 있습니다.

sizeof() 함수는 현재의 자료형의 크기를 구하기 위해서 사용하는 함수이고,

strlen() 함수는 문자열의 크기를 구하기 위해서 사용하는 함수입니다.

두 함수의 역할이 완전히 다르기 때문에 절대 헷갈리면 안 됩니다.

 

다음 코드를 입력하고 실행 결과가 어떻게 나오는지 확인해 보세요.

문자형 포인터의 sizeof(ptr1)의 결과와 문자형 포인터의 strlen(ptr1)의 결과는

다음과 같이 서로 다른 것을 확인할 수 있습니다.

ptr1의 sizeof() 결과 : 4

ptr1의 strlen() 결과 : 6

sizeof() 함수는 문자형 포인터의 자료형의 크기이므로 포인터의 크기인 4바이트를 출력하지만,

strlen()함수는 문자형 포인터가 가리키는 문자열의 크기를 의미하므로 6바이트가 출력되는 것입니다.

단 실제 데이터는 5바이트이지만 문자열의 마지막에 '\0' 문자가 추가되어서 모두 6바이트가 됩니다.

 

[ 040 ] - 구조체 포인터를 사용할 때는 sizeof(*ptr) 형식을 사용하자.

sizeof() 함수를 사용할 때 또 하나 저지르기 쉬운 실수는 구조체 포인터의 경우입니다.

다음 코드를 입력하고 실행해 보세요.

마지막 세 줄의 printf() 문을 확인해 보면 sizeof(ptr), sizeof(*ptr), sizeof(NODE)로 서로 다른데

결과는 어떻게 나오는지 다음의 결과를 살펴봅시다.

ptr의 sizeof() 결과 : 4

ptr의 sizeof() 결과 : 12

NODE의 sizeof() 결과 : 12

 

sizeof(ptr)는 구조체, 문자에 상관없이 포인터의 크기인 4바이트를 돌려줍니다.

실제 구조체의 크기를 구하려면 sizeof(*ptr)나 sizeof(NODE)로 알 수 있습니다.

이 가운데 특히 sizeof(*ptr)를 사용해야 하는데 sizeof(ptr)를 사용해서 실수하는 경우가 있으므로 주의하기 바랍니다.

Posted by devanix
2011. 9. 14. 16:47

[ 021 ] - 함수의 매개변수를 사용하여 연산하지 말자.

초보 프로그래머들의 실수 가운데 하나는 함수의 매개변수를 for 문이나 while 문의 제어 조건을 이용한다는 것입니다.

하지만 고수 프로그래머들은 매개변수를 사용하여 함수 내부의 연산을 하지 않습니다.

매개변수의 값을 연산할 필요가 있다면, 함수 내부에 별도의 변수를 하나 선언해서 그 변수에 매개변수의 값을 받아 연산을

하는 것이 바람직합니다.

매개변수로 연산을 하지 않는 것은 디버깅 시간을 줄여주며 매개변수 때문에 일어날 수도 있는

논리적인 오류를 미리 예방할 수 있습니다.

 

[ 022 ] - 함수의 매개변수를 검사하자.

함수가 호출되면 고수 프로그램의 코드는 함수에서 사용하는 매개변수의 값을 검사합니다.

매개변수의 값이 정상적인 값인지 먼저 확인하고 나서 실제 함수 내부의 코드를 실행하는 것이죠.

이와 같은 과정은 특히 포인터를 매개변수로 사용할 때 치명적인 메모리 에러를 예방할 수 있습니다.

여러분도 포인터를 매개변수로 사용할 때 잊지 말고 검사 코드를 추가하기 바랍니다.

 

[ 023 ] - 함수의 반환 값을 이용하여 함수의 실행 여부를 판단하자.

초보 프로그래머들은 함수의 반환 값을 단지 함수의 연산 결과를 저장하는 용도로만 사용하지만,

프로그래밍 고수들은 함수의 반환 값을 사용하여 호출한 함수가 정상적으로 실행되었는지

아니면 중간에 어떤 오류가 있었는지 판단합니다.

물론 모든 함수의 반환 값을 함수의 실행 여부를 판단하는 용도로 사용할 수는 없지만,

함수의 실행 여부가 전체 프로그램에서 중요한 역할을 한다면 함수의 반환값을 사용하여

함수의 실행 여부를 판단하는 습관을 들이세요.

 

[ 024 ] - printf("[%s] [%s] [%d]\n", _FILE_, _FUNCTION_, _LINE_);을 적극적으로 활용하자.

프로그램의 코드가 길어지고 논리적으로도 복잡해지면 디버깅을 하기가 좀처럼 쉽지 않습니다.

요즘 사용하는 비주얼 C++와 같은 개발 도구는 디버깅을 쉽게 할 수 있는 기능이 갖추어져 있지만,

모든 개발을 비주얼 C++만으로 할 수는 없기 때문에 개발 환경이 윈도우, 리눅스에 상관없이

다음과 같은 코드를 사용해서 디버깅을 좀더 쉽고 효율적으로 할 수 있습니다.

printf("[%s] [%s] [%d]\n", _FILE_, _FUNCTION_, _LINE_);

위의 코드가 갖는 의미는 현재 위치에서 사용하는 파일의 이름과 함수의 이름, 그리고 소스 코드의 행 번호를

화면에 출력하라는 의미입니다.

예를 들면 본문의 예제처럼 프로그램의 실행을 어떤 오류 때문에 중지해야 할 경우 어느 모듈의 어느 부분에서

중단되었는지 알기 어렵기 때문에 프로그램의 실행을 중단해야 하는 코드에 위의 코드를 넣어주면

아래 결과 화면에서 현재 어떤 위치에서 실행을 중단했는지 쉽게 알 수 있습니다.

[f:\test1\main.c][main][23]

위의 결과는 현재 코드 위치가 main.c 소스 파일의 main 함수에서 23행이라는 의미입니다.

 

[ 025 ] - 초보 프로그래머들의 실수 열 가지를 피하자.

초보 프로그래머들은 아직 프로그래밍 경험이 많지 않기 때문에 자신이 만든 프로그램이 특별한 에러 없이 실행 파일이

만들어진다는 것만으로도 감격해 합니다. 하지만 실행 파일은 컴파일이 되는 정도를 나타내 줄 뿐이고 프로그램이

정상적으로 실행될 것이라는 보장을 해 줄 수는 없습니다.

 

초보 프로그래머들이 흔히 저지르기 쉬운 실수 가운데 아래와 같이 컴파일러에서 에러로 표시하지 않는 열 가지.

① 함수의 매개변수의 이름이 같은 경우

② 함수의 반환 값과 반환 값을 저장하는 변수의 자료형이 다른 경우

③ 제어변수의 연산을 제대로 하지 않는 경우

④ 변수의 이름이 a, b, c나 i, j, k처럼 의미 없는 경우

⑤ 음수 값을 가질 수 없음에도 음수 검사를 하는 경우

⑥ 포인터를 초기화하지 않고 사용하는 경우

⑦ 배열의 크기나 malloc()의 크기를 임의로 잡는 경우

⑧ if-else 문의 괄호를 제대로 설정하지 않는 경우

⑨ 0과 1의 설정이 잘못된 경우

⑩ == 연산자 대신 = 연산자를 사용한 경우

 

[ 026 ] - 삼항 연산자를 적극 활용하자.

삼항 연산자를 리눅스 관련 프로그램 코드에서 쉽게 볼 수 있는 연산자이며 프로그래밍의 고수라고 하는 사람들의

소스 코드에서도 쉽게 볼 수 있는 연산자입니다.

삼항 연산자는 여러 줄로 구현하는 if~else 문을 한 줄로 줄여서 표현하는 효과도 있지만, if~else 문으로 구성된 코드보다

훨씬 더 빠르게 소스 코드를 이해할 수 있도록 도와줍니다.

삼항 연산자의 사용법을 조금만 익히면 프로그래밍에 많은 도움이 될 것입니다.

 

일반적인 예:

max = (a > b) ? a : b;

 

 

[ 027 ] - 연산자의 우선순위를 항상 고려하자.

대부분의 프로그래밍 책에서 언급하는 연산자의 우선순위를 가볍게 생각하는 프로그래머가 많습니다.

특히 초보 프로그래머들은 포인터나 구조체 등의 고급 기능을 이해하는 데에는 많은 주의를 기울이지만 연산자 우선순위에

대한 내용은 마치 사칙연산(+, -, *, /)정도의 수준에서 이해하는 경우가 많습니다.

오히려 포인터나 구조체, 메모리에 관련된 고급 기술은 시간이 지나면서 실무를 통해 자연스럽게 익힐 기회가 있지만

연산자 우선순위는 기억해 두고 있지 않으면 사용할 수 없기 때문에 영어 단어를 암기하듯이 항상 외워두어야 합니다.

 

다음은 꼭 기억해두어야 할 우선순위를 나열한 표입니다.

우선순위

연산자 종류

연산자

높음

낮음

괄호∙배열∙구조체 연산자

() [] -> .

단항 연산자

! ~ ++ -- (형 변환 연산자) * & sizeof

곱셈, 나눗셈 연산자

* / %

덧셈, 뺄셈 연산자

+ -

시프트 연산자

<< >>

비교 연산자

< <= > >=

비교 연산자

== !=

비트 연산자

& ^ |

논리 연산자

&& ||

삼항 연산자

? :

대입 연산자

= += =+ *= /= &=

대입 연산자

> >= < <= &= ^= |=

 

 

[ 028 ] - x++와 ++x의 차이를 정확하게 알자.

고수라고 인정받는 개발자들도 x++와 ++x의 차이를 명확하게 알지 못하는 경우가 많습니다.

++ 연산자가 단순히 증감 연산자라는 정도는 알지만 x++와 ++x가 완전히 다른 결과를 가져다 준다는 사실은 잘 모릅니다.

작은 표현의 차이지만 결과의 차이는 큰 증감 연산자의 사용에 대해서 확실하게 이해합시다.

 

다음 코드를 직접 입력해서 실행해 보세요.

위의 코드를 실행한 결과는 다음과 같습니다.

z : 1 x : 2

z : 2 y : 2

 

먼저 8행의 z = x++;의 결과에서 ++연산자보다 = 연산자가 먼저 실행된 것을 알 수 있습니다.

즉 다음과 같이 x 값을 z에 대입한 후 x의 값이 하나 증가하는 순서로 연산이 실행됩니다.

z = x;

z = x + 1;

 

그런데 11행의 z = ++y의 결과를 살펴보면 = 연산자보다 ++연산자가 먼저 실행된 것을 알 수 있습니다.

즉 다음과 같이 y의 값을 하나 증가시킨 후 증가한 y값을 z에 대입하는 순서로 연산이 실행됩니다.

y = y + 1;

z = y;

 

++xx++의 차이는 완전히 다른 결과를 가져옵니다.

따라서 연산자의 위치와 정확한 사용 방법을 이해하는 것은 프로그래밍에서 아주 중요합니다.

 

[ 029 ] - 연산자가 복잡해지면 괄호를 사용하자.

주변에 영어를 잘하는 사람을 보면 우리가 중학교 때 배운 문장이나 단어만으로도 외국인과 어려운 없이 대화하는 것을

볼 수 있습니다. 하지만 어려울 것 없는 단어들을 조합해서 문장을 만들고 이 문장의 형태로 말하거나 듣는 것은

쉽지 않습니다. 프로그래밍도 영어를 구사하는 것과 비슷합니다. 잘 사용하지도 않는 연산자나 복잡한 알고리즘을

모두 외운다고 해서 프로그래밍의 고수가 되는 것이 아닙니다.

오히려 고수들의 프로그램은 의외로 단순하고 간단 명료한 경우가 대부분인데,

하나의 특징은 고수 프로그래머의 코드에는 다른 사람이 봤을 때 이해하기 어렵거나 모호하게 기능을 구현한 코드가

없다는 것입니다. 이와 같이 고수 프로그래머들이 구현한 프로그램에서 자주 볼 수 있는 것이 초보 프로그래머가

복잡하고 길게 나열해서 구현하는 기능을 간결하게 바꾸고 깔끔하게 정리해서 구현한다는 것입니다.

이와 같이 복잡하고 이해하기 힘든 코드를 간결하고 보기 쉽고 간단하게 정리하는

노하우 가운데 하나가 연산자가 복잡해지면 괄호로 묶어서 우선순위를 명확하게 구분하는 습관을 갖는 것입니다.

 

[ 030 ] - 새로운 자료형을 선언할 때는 typedef를 사용하자.

typedef를 사용하면 새로운 연산자를 만들어 사용할 수 있습니다.

typedef는 구조체형을 선언할 때 가장 많이 사용합니다.

typedef struct _Node {

        char *data;

        struct _Node *Next;

} NODE;

 

typedef는 구조체형을 선언하는 것 이외에 복잡한 부분을 한눈에 알 수 있도록 사용하는 경우도 있습니다.

예를 들면 unsigned char A;라고 하면 A의 자료형이 unsigned char형이지만 실제 어떤 목적으로 사용되는

변수인지 알기 어렵기 때문에, 이 때 typedef를 이용해서 unsigned char를 SENSOR로 표현해 두면

변수 A가 센서의 역할을 한다는 것을 쉽게 알 수 있습니다.

typedef unsigned char SENSOR;

SENSOR A;

 

정수형 데이터를 갖는 학점을 사용하는 경우라면 다음과 같이 typedef를 사용할 수 있습니다.

typedef unsigned int GRADE;

 

GRADE Korean;

GRADE English;

GRADE Math;

 

GRADE로 선언된 Korean, English, Math라는 변수만 보고도

이 변수들의 사용 목적이 학점을 표시하기 위한 것임을 쉽게 알 수 있습니다.

Posted by devanix
2011. 9. 14. 16:13

[ 011 ] - 공백문자를 사용하여 코드를 보기 좋게 만들자.

프로그래밍을 시작한 지 얼마 안 되는 사람들은 소스 코드를 작성하기에 급급해서 다음 코드와 같은 형태로

구성하는 습관이 있습니다.

위의 코드에서 공백문자만 추가하면 다음과 같습니다.

연산자와 변수 사이에 단순히 공백문자 하나만 더 추가했을 뿐인데도 위의 코드보다 훨씬 보기가 편할 것입니다.

사실 실행 결과는 위의 코드나 아래 코드나 똑같지만 소스 코드를 작성하는 사람들의 의무 가운데 다른 사람이

자신의 소스 코드를 보더라도 쉽게 이해할 수 있도록 해야 한다는 조항이 있다는 것을 명심하세요.

 

[ 012 ] - 변수를 사용할 때는 수직으로도 정렬하자.

이 방법도 신입 사원들에게 제가 권장하는 방법 중의 하나입니다.

위와 마찬가지로 이렇게 하지 않아도 사실 상관은 없지만 이것은 소스 코드를 한눈에 보기에 더 좋은 방법입니다.

소스 코드를 수직으로 줄을 맞춘다는 의미는 보통 변수들, 특히 구조체의 멤버나 구조체 포인터를 사용하여

멤버의 값을 초기화할 때 각각의 이름의 길이에 따라 소스 코드가 들쑥날쑥 하게 됩니다.

이런 경우가 한두 줄이면 그다지 상관없겠지만 그 수가 좀 많아지면 보기에 안 좋겠죠.

다음은 수직으로 정렬되지 않은 소스 코드입니다.

대략 위와 같은 코드가 있다고 가정하면 코드에는 큰 문제는 없어 보입니다.

그러나 위의 코드를 다음과 같이 수직으로 정렬하면 어떨까요?

물론 수직으로 정렬할 때도 <Spacebar> 가 아닌 <Tab> 으로 열을 맞춥니다.

위의 코드는 그다지 길지 않기 때문에 특별히 표가 나지는 않지만, 실무에서 이와 비슷한 작업을 하다 보면

초기화 변수만 수십 개를 쓰는 경우도 생깁니다. 이런 경우에는 위의 코드와 같이 수직으로 정렬을 해두면

나중에 코드를 보거나 디버깅할 때 도움이 됩니다.

 

[ 013 ] - 전역 변수의 사용을 최대한 피하자

전역 변수도 나름대로의 장점이 있기 때문에 필요한 경우에는 사용해야 하지만 되도록이면 매개변수나

구조체를 이용하여 전역 변수의 기능을 대신하는 것이 좋습니다. 지금보다 나은 실력의 프로그래머가 되려면

전역 변수의 사용을 최대한 피하는 것이 좋은 프로그래밍 습관입니다.

 

[ 014 ] - 정적 변수를 활용하자.

우리가 일반적으로 알고 있는 static의 기능 외에 다른 파일의 변수의 값을 변경하거나 아무 제약 없이

접근하는 것을 막을 수 있습니다. 이와 같은 기능을 C++나 자바와 같은 객체 지향 프로그래밍에서 private이라는

키워드로 제공합니다. 비록 C 언어에서 완벽하지는 않아도 정적 변수를 사용하면 다른 파일이 변수 값을 수정할

때 일어날 수 있는 버그나 오류를 막을 수 있습니다.

예를 들면 앞의 예제에서 SetAge()와 같은 함수는 사람의 나이를 설정하는 기능이므로 음수가 절대 있을 수 없습니다.

이때 다음과 같이 SetAge() 함수를 수정하면 무의식적으로 저지를 수 있는 실수를 막을 수 있습니다.

위의 코드처럼 매개변수 num의 값이 0보다 큰 경우에만 정적 변수 Age에 대입하고 그렇지 않은 경우에는

에러 코드를 출력하게 할 수 있습니다.

 

[ 015 ] - 정적 함수로 접근 권한을 주자.

정적 변수와 마찬가지로 정적 함수를 사용하면 함수가 정의된 파일 안에서만 함수를 사용할 수 있고

다른 파일에서 접근하는 것을 허용할 수 있습니다. 이와 같은 방법은 프로그래밍의 모듈화를 극대화할 수 있으며

디버깅에 들어가는 시간과 노력을 줄일 수 있습니다.

많은 사람들이 C++가 C 언어보다 우수하다고 말하는 이유 가운데 하나가 바로 접근 권한을 줄 수 있기 때문인데,

실제 C 언어에서도 완벽하지는 않지만 파일(모듈) 단위로 접근 권한을 줄 수 있습니다.

 

[ 016 ] - 변수, 함수 이름은 누구나 이해하기 쉽게 만들자.

여러 명이 공동으로 프로젝트를 진행하는 경우에는 함께 프로젝트에 참여하는 사람들이 미리 이름 규칙을 협의하는

것이 좋습니다. 묵시적으로는 헝가리언 표기법을 따르겠지만 프로젝트의 성격에 따라 특별히 이름을 정하는 것에 대한

규칙을 정해 두는 것도 좋습니다.

예를 들면 플래그 기능으로 사용하는 변수는 f를 사용하며, 포인터는 p, char형의 경우는 변수 이름 앞에 c를 붙이고 문자열인 경우는 s를 붙인다고 미리 협의를 하면 각 파트에서 이름을 결정할 때 도움이 될 수 있습니다.

이와 같은 형식을 사용하면 변수 이름만 보고도 그 변수의 자료형을 예측할 수 있기 때문에 자료형이 달라서 생기는 오류나 버그를 미리 막을 수 있습니다.

 

[ 017 ] - 같은 자료형을 서로 다른 목적으로 사용하려면 typedef를 이용해서 새로운 자료형으로 정의해서 사용하자.

C 언어에서 제공하는 자료형은 한계가 있기 때문에 서로 다른 목적으로 사용하는 변수라고 해도 같은 자료형으로

데이터를 표현하기가 쉽습니다. 대표적인 것이 정수형인 int형과 문자형인 char형입니다.

소스 코드가 복잡하지 않고 길지 않으면 같은 자료형을 사용해도 크게 상관이 없지만, 소스 코드가 복잡하고

소스 코드 파일의 개수가 많을 때에는 같은 자료형을 사용하면 아무리 변수의 이름을 쉽게 표현해도 프로그램을

이해하기 어렵습니다.

이때 문제를 해결하기 위해서 사용할 수 있는 것이 typedef입니다.

다음과 같이 같은 정수형, 문자형이라도 필요한 목적에 맞게 정의해서 사용하면 소스 코드를 이해하기가 훨씬 쉽습니다.

 

 

[ 018 ] - const 포인터의 숨겨진 기능을 알아 두자.

C 언어에서 제공하는 const는 단지 상수 값을 사용하는 기능만 있는 것은 아닙니다.

특히 포인터와 함께 const를 사용할 때는 다음과 같은 숨겨진 기능이 있습니다.

 

① const 포인터가 가리키는 값을 변경할 수 없다. 단 const 포인터의 주소는 변경할 수 있다.

② const 포인터는 일반 포인터로 할당될 수 없다.

 

이 두 가지 특징은 포인터를 사용할 때 생길 수 있는 논리적인 버그를 어느 정도 예방할 수 있기 때문에 꼭 기억해 두세요.

 

[ 019 ] - 메모리 주소를 출력할 때에는 %#x 형식 지정자를 사용하자.

포인터의 주소를 출력할 때 다음과 같이 printf() 함수 안의 형식 지정자를 "%#x"로 사용하는 경우가 있습니다.

printf("NormalPtr[%#x] : %s\n", NormalPtr, NormalPtr);

보통 초보 프로그래머의 경우 포인터의 주소를 출력할 때 %p를 사용합니다.

%p의 경우는 16진수 형태로 포인터의 주소를 출력하라는 의미입니다.

따라서 %p를 사용하여 출력하면 다음과 같은 결과가 나타납니다.

NormalPtr[00424678] : RYAN

이때 %#x를 사용하면 다음과 같은 결과가 나타납니다.

NormalPtr[0x424678] : RYAN

%p를 사용하는 것보다 %#x를 사용하는 것이 포인터의 주소를 출력하는 데 훨씬 보기가 편하다는 사실을 알 수 있습니다. 또 어떤 경우에는 %#x 대신 %x를 사용하기도 하는데 %x를 사용하면 다음과 같은 결과가 나타납니다.

NormalPtr[424678] : RYAN

세 개의 형식 지정자 모두 포인터의 주소를 출력하는 형식으로 사용하지만 위의 세 가지 표현 가운데

실제 프로그래밍에서 사용하는 메모리 주소의 형식과 가장 맞는 표현이 0x424678의 형태이기 때문에

%#x를 사용하는 것이 좋습니다.

 

[ 020 ] - 함수를 사용할 때 좋은 습관 다섯 가지를 지키자.

함수가 얼마나 중요한 역할을 하는지 프로그래밍을 다루다 보면 뼈저리게 느낄 수 있습니다.

하지만 어떤 규칙도 없이 함수를 사용하면 소스 코드의 양이 많아지고 프로그램이 복잡해져서 상당한 곤란을 겪습니다.

따라서 다음 다섯 가지 습관을 지키어 함수를 사용하세요.

 

① 프로토타입을 명시하자.

② 반환 타입을 정확하게 기입하자.

③ 함수의 이름은 기능을 알기 쉽게 작성하자.

④ 함수의 매개변수를 잘 활용하자.

⑤ 하나의 함수는 한 가지 기능만 구현하자.

 

Posted by devanix
2011. 9. 14. 16:11

[ 001 ] - 모듈 ∙ 인터페이스 등을 확실하게 정의한 후 시작하자.

프로그래밍을 시작하기 전에 프로젝트에 참여하는 다른 프로그래머들과 충분히 의논하고 인터페이스를 확인하는

작업을 반드시 거쳐야 합니다.

 

[ 002 ] - 상태별로 제어하는 습관을 기르자.

실제 기업체나 연구소에서 상태 제어 프로그래밍 방법을 이용하여 실무가 이루어지고 있으므로 상태별로 제어하는 습관을 기르는 것이 중요합니다. 대부분의 프로그램은 여러 상태(status)로 이루어져 있고 각각의 상태를 제어하는 것은 일반적인 프로그래밍 방법 가운데 하나입니다.

 

[ 003 ] - 일어날 수 있는 모든 입출력을 점검하자.

프로그램을 설계할 때에는 일어날 수 있는 모든 입출력을 점검해야 합니다. 전체 몇 가지의 경우의 수가 일어날 수 있는지 확인하고, 어떤 경우에만 해당하는 사항인지 아니면 모든 경우에 대해서 대응해야 하는지 점검해야 합니다.

 

[ 004 ] - 프로젝트의 단계를 하나씩 밟자.

① 전체적인 구조 파악

② 세부적인 알고리즘 검토

③ 실제 구현

④ 디버깅과 수정

 

각 단계 가운데 어느 하나라도 그냥 지나치지 않아야 하며, 앞으로 일어날 수 있는 문제가 무엇인지 예측하고

미리 보완하는 습관을 가져야 합니다.

 

[ 005 ] - 필요하면 개발 환경을 직접 만들 수 있어야 한다.

어디에도 현재 진행하는 프로젝트에 최적화된 개발 환경은 없습니다. 프로젝트를 진행하면서 필요에 따라 프로그램을 테스트하거나 검증할 수 있는 도구를 직접 만들 수 있다면 이것은 전체적인 생산성을 높여 줄 것입니다.

 

[ 006 ] - 프로그램은 결과도 중요하지만 과정도 중요하다.

초보 프로그래머가 쉽게 범하는 실수 가운데 하나는 프로그램은 결과가 중요하다고 생각하는 것입니다. 물론 원하는 결과가 제대로 나오는 것도 중요하지만 결과가 제대로 나오더라도 프로그램이 엄청나게 많은 메모리를 차지하거나 프로그램의 실행 시간이 밥 한 끼를 먹어도 충분할 만큼 길다면 결코 좋은 프로그램을 만들었다고 볼 수 없습니다. 따라서 프로그램은 결과도 중요하지만 다음과 같은 중간 과정도 중요합니다.

 

① 코드의 최적화

메모리, CPU의 시간과 같은 리소스(resource)를 낭비하지 않고 적절하게 사용하는 최적화

② 코드의 재사용성

다른 사람이 자신의 코드를 사용할 때 별다른 수정 없이 사용할 수 있도록 구성하는 모듈화

③ 코드의 가독성

다른 사람이 소스 코드를 쉽게 이해할 수 있게 하는 가독성

 

[ 007 ] - if 문과 case 문을 적절하게 섞어서 사용하자.

if 문과 case 문은 서로 비슷한 기능을 하지만 사용하다 보면 서로 다른 기능을 할 때가 있습니다.

특히 수십 개의 if 문이 필요할 때에는 오히려 case 문이 더 적합한 경우가 있기 때문에 프로그램의 효율성을 위해서

프로그램 코드에서 if 문과 case 문을 섞어서 사용하는 습관을 갖는 것이 좋습니다.

 

[ 008 ] - 헤더 파일을 한 번만 인클루드하는 노하우를 익히자.

여러 사람이 공동으로 하나의 프로젝트를 진행하다 보면 헤더 파일을 인클루드하는 문제가 자주 생깁니다.

따라서 다음의 두 가지 방법 가운데 하나를 이용하면 인클루드 문제 때문에 일어나는 링크 에러를 막을 수 있습니다.

 

① #ifndef ~ #ifdef ~ #endif 방법

② #pragma once 방법

 

위의 두 방법 가운데 첫 번째 방법을 이용하면 컴파일러에 상관없이 어느 개발 환경에도 적용할 수 있기 때문에 나중에 다른 플랫폼에 소스 코드를 쉽게 이식할 수 있습니다.

 

[ 009 ] - 당근과 채찍을 두루 사용하는 프로젝트 매니저가 되자.

프로젝트 매니저는 근태 관리나 하고 아랫사람의 납기를 다그치기만 하는 군대의 고참과 같은 존재가 결코 아닙니다. 프로젝트에서 각각의 담당자들이 맡고 있는 분야 전체를 꿰뚫고 있고, 그 프로젝트에서 생길 수 있는 문제점을 예상하고,

그에 대한 대비책을 마련해야 하는 중요한 역할을 하는 사람이 프로젝트 매니저입니다.

따라서 프로젝트 매니저는 인간관계 뿐만 아니라 기술적인 노하우도 다른 프로그래머에 비해 탁월해야 합니다.

그저 시간이 흘러서 대리가 과장이 되고, 과장이 차장이 되는 그런 직책이 절대 아닙니다.

오히려 하나의 프로젝트를 진행하면서 가장 바쁘고, 힘들게 일하는 사람이 바로 프로젝트 매니저입니다.

또 프로젝트 매니저는 하나의 프로젝트를 성공적으로 끝내기 위해서 각 담당자들 개개인의 역량을 파악한 다음

업무를 진행시켜야 하며 그와 함께 적절한 칭찬을 해주어야 합니다. 실제 엔지니어들이 업무에서 갖는 가장 큰 불만은

칭찬에 인색한 프로젝트 매니저라고 합니다. 업무의 결과에 대해 정확한 당근과 채찍은 필수입니다.

채찍만 휘둘러 대는 프로젝트 매니저의 업무 습관은 프로젝트가 실패로 향하는 지름길입니다.

 

[ 010 ] - 주석은 필요한 정보만 간단하게 서술하자.

음식도 질에 상관없이 양만 많으면 언젠가는 탈이 나게 마련이듯이 프로그래밍도 마찬가지입니다.

쓸데없이 양만 많은 주석은 오히려 소스 코드의 이해에 방해를 줄 뿐만 아니라 소스 코드를 수정할 때에도 많은 주석을 함께 수정해야 하므로 번거롭기만 합니다.

소스 코드의 신빙성과 효율성을 위해서 보여주기만을 위한 소설식 주석보다 좀더 기능에 치중해서 간결하게 설명하는 것이 좋습니다.

주석을 작성할 때 다음과 같은 정보만 전달하는 것이 가장 효과적입니다.

- 현재 작업하고 있는 모듈 이름

- 모듈의 인터페이스(매개변수, 반환 타입 등)

- 모듈의 최초 작성자

- 모듈의 마지막 수정자

- 모듈의 작성 날짜와 가장 최근의 수정 날짜

그 밖에 소스 코드 안에서 코드 옆에 붙이는 주석은 필요한 경우에 함수 호출 부분에서 그 함수의 기능을 간단하게

나타낼 수도 있으며 특별히 중요한 변수인 경우에는 주의를 요한다는 설명을 곁들일 수 있습니다.

Posted by devanix
2011. 9. 14. 16:10

 

[ 책 소개 ]

코드는 짧게, 용량은 작게, 속도는 빠르게! 작은 차이로 큰 효과를 내는 좋은 프로그래밍 습관 61가지를 알려주는 책. 초보 프로그래머들이 프로그래밍 전문가로 거듭나는 데 도움을 줄 것이다. 

특히 코드의 간결성, 재사용성, 최적화, 메모리 관리, 에러 방지 등 실무에 꼭 필요한 내용을 중심으로 'How to programming'에 대한 정확한 해법을 제시하기 때문에 초보 프로그래머들은 이 책을 보는 것만으로도 실무 감각을 쉽게 익힐 수 있다. 

또 필자가 엄선한 좋은 프로그래밍 습관을 통해 같은 코드를 작성하더라도 전체 소스 코드의 흐름을 파악해서 가장 적합하고 효율적인 명령으로 표현하는 방법을 가르쳐 준다.

 

[ 목 차 ]

01장 코드를 만들기 전 사전 협의는 반드시 필요하다

02장 상태 제어를 잘하는 것이 고수가 되는 지름길이다

03장 뛰어난 목수는 연장을 직접 만들어 사용한다

04장 제대로 사용한 case 문 하나, 열 if 문 안 부럽다

05장 전처리기를 이용하여 다중 파일을 사용한다

06장 칭찬은 개발자를 춤추게 한다

 

특집 프로그래밍에 관한 일반적인 궁금증

 

07장 주석이 없는 코드를 이해할 수 있는 사람은 없다

08장 지뢰밭 코드

09장 C 언어의 객체 지향 개념, static

10장 변수와 자료형은 함께 생각한다

11장 데이터의 자물쇠 기능, const

12장 함수의 다섯 가지 사용 규칙

 

특집 프로그래밍 언어의 종류에 관한 궁금증

 

13장 함수 튜닝의 좋은 습관 세 가지

14장 초보들의 실수 열 가지

15장 연산자를 효과적으로 사용한다

16장 열거형을 사용하는 재미

17장 연산자를 사용할 때 주의할 점

18장 메모리, 재대로 알고 사용하자

 

특집 하드웨어 프로그래밍에 관한 궁금증

 

19장 배열과 포인터의 찰떡궁합

20장 배열보다 더 강력한 포인터의 고급 기능

21장 포인터를 사용할 때 이것만큼은 주의하자

22장 포인터를 이용한 메모리의 할당과 해제

23장 포인터를 이용한 메모리의 복사와 초기화

24장 함수 포인터의 오묘함에 대하여

 

특집 비주얼 C++에 관한 궁금증

 

25장 구조체만 잘 써도 B 학점은 받는다

26장 고수들이 사용하는 구조체 활용 노하우

27장 코드 안에서 메모리를 공유하자

28장 애벌빨래, 전처리기

29장 초보는 모르는 개발 도구, Make

30장 프로그래밍의 마술사, 디버깅

 

Posted by devanix