3.20(화) 함수의 설계 원칙 정리

from Study/C언어 2007/03/20 18:30 view 28441
함수를 작성하는 문법과 호출하는 방법, 인수를 받아들이고 리턴하는 방법을 익히는 것은 그다지 어렵지 않지만

정말로 함수답게 잘 나누고 디자인하는 것은 무척 어렵고 단기간에 체득되지 않는다. 함수는 프로그램을 구성하는

단위로서 잘 나누어 놓으면 프로그램의 구조가 탄탄해지고 확장하기도 쉽고 재사용성도 좋아진다.

함수디자인은 오로지 많은 분석과 실습만으로 얻어지는 경험이다. 꾸준한 연습만이 해결책이라 할수있다.


함수를 잘 만드는 기본적인 지침

1. 함수의 이름을 최대한 설명적으로 작성하여 이름만으로 무엇을 하는 함수 인지, 이왕이면 어떻게 쓰는 것인지도

알 수 있도록 한다. Score, Draw, Test 라는 이름보다도 GetScore, DrawScreen, TestGameEnd와 같이 기능을

명시해주는게 보기도 좋고 효율적이다. 자바를 이용하여 코딩해 보면 GET,SET 을 주로 함수앞에 써서 값을

얻거나 정해줄때를 명확히 표현해준다.


2. 두번이상 중복된 코드는 반드시 함수로 분리한다. 3번도 아니고 2번인 이유는 해당 동작을 수정해야 할때를

생각해 보면 된다. 만일 2번의 중복된 코드가 있는데 이 코드가 논리적으로 맞지 않다면 수정을 해줘야 할 것이다.

그러나 일일이 찾아가서 두개를 모두 고친다는 보장은 없다.(나같은 놈은...특히) 실수로 한 곳을 고치지 않으면

이것이 바로 버그의 원흉이 된다. 또한 두번 중복되는데 3번은 안되고 그이상은 안되겠는가 .. 미래를 위해서라도..


3. 반복되지 않더라도 한 단위로 볼 수 있는 작업은 함수로 만든다. 설사 이 함수를 딱 한번만 호출하고 다른

곳에서 호출할 확률이 희박하더라도 이렇게 하는 것이 좋다. 코딩을 하다보면 출력을 해야 하는 부분이 있고

입력을 해야 하는 부분이 있을 것이다. main 함수내에서 이를 전부 처리해도 문제는 없다.

하지만 어디가 출력부인지 입력부인지 가독성이게 표시를 할 수 있나가 문제이다. 물론 주석으로 일일히

표시해 줘도 되지만 가독성이 떨어 진다. 함수로 객체와 비스무리하게 나눠 주면 재사용도 좋고 보수하기도 좋고..


4. 함수는 한번에 하나의 작업만 해야 한다. 함수는 프로그램을 구성하는 부품이며 부품이란 전체를 구성하는

원자적인 단위이다. 함수 하나가 출력도 하고 입력도 받는다면 굳이 함수를 쓸 필요가 있을까 고장이 나도

구조가 간단한 부품에서 나야 하지 않을까 .. 함수도 마찬가지라 생각이 든다.


5. 입력과 출력이 직관적이고 명확해야 한다. 인수는 함수에게 주어지는 작업거리인데 함수가 하는 일에

꼭 필요한 정보만 최소한의 인수로 받아들여야 한다.

void CheckStr(char *str, int len);

과 같이 함수에 인수를 넘기고자 했을 때 굳이 문자열의 길이 len을 넘겨주지 않아도 strlen이라는 함수로

문자열의 길이를 판별할 수 있다는 것이다. 불필요한 입력이 생겨버린 셈이다.


6. 함수는 자체적으로 에러 처리를 해야 한다. 파일을 처리한다든지(fopen) 초를 계산한다든지(mktime)

할 때 에러가 발생하면 이들은 -1 , null 을 반환 해준다. 이러한 반환값을 함수내에서 에러 처리를 해줘서

어떤 프로젝트로 가져 가든 별도의 수정없이 재사용 가능한 부품이 되게 해줘야 한다. 만일 에러처리를 모두

메인 함수가 담당한다면 이 또한 함수의 사용의미를 잃어 버리는게 아닐까..
Tag |

선언과 정의는 비슷한 의미 같지만 함수나 변수등에서 사용되면 그 의미는 달라진다.

선언(Declartion) : 컴파일러에게 대상에 대한 정보를 알린다. 함수가 어떤 인수들을 전달받으며

어떤 타입을 리턴하는지를 알리는 원형 선언(프로토타입)이 대표적인 선언이다. 컴파일러에게 정보만 제공하는

것이므로 본체를 가지지 않으며 실제 코드를 생성하지도 않는다. 그래서 다음처럼 여러번 중복되어도 상관없다.

int MAX(int a, int b);
int MAX(int a, int b);

정의(Definition) : 대상에 대한 정보로부터 대상을 만든다. int i; 정의문에 의해 4바이트를 할당하며

int Max(int, int){ } 정의로부터 함수의 본체를 컴파일하여 코드를 생성한다. 정의는 변수의 타입, 함수의
 
인수목록을 컴파일러에게 알려 주기도 하므로 항상 선언을 겸한다.

선언 역할-알린다,     메모리-사용안함,  정보의 완전성-불완전해도 됨,      중복가능석-가능
정의 역할-생성한다,  메모리-할당,        정보의 완전성-항상 완전해야 됨,  중복가능성-불가능


하지만 실제로는 별 구분없이 사용되고 있다. 따라서 이러한 명칭적 정의가 있다는 것만 알고 있자..

#define A 1000     // 보통 디파인한다.정의한다로 하지만 메모리 할당을 받지 않으므로 선언이다.
int i;                   // 인트형을 선언한다 이런 말을 많이 쓰지만 메모리를 잡아 먹으므로 정의이다.
extern int j;        
// 외부 함수(모듈)에 정의된 int형을 선언하는거라고 해야 정확한 표현(?) 이다.

3.20(화) 매크로함수..

from Study/C언어 2007/03/20 15:02 view 25323

프로그램 내에서 자주 사용하는 상수나 반복문을 또 쓰기 싫거나 const상수를 만들고 싶을때

#define 문을 써서 표현 하는것이 좋다.

지금 제일 많이 쓰는 #define MAX 100 이런 문장은 프로그램의 최대값을 변경 하고 싶을 때

용이하게 할 수 있다. 매크로 라는 기능으로도 유용한데 그건 아랫글 참고..

2007/03/20 - [Study/CNX] - 3.20(화) 스피커음 내보기. // 이전글넣기 플러그인 너무 좋다. ^_^

매크로 함수는 함수를 흉내 내서 인수를 받아들이고 매크로 실행 후 계산 결과를 리턴한다.

하지만 이 리턴하는게 말이 리턴이지 문장의 치환에 불과하다.

함수처럼 연산을 실행한 후에 결과값을 반환하는게 아니라는 것이다. (맨날 헷갈려ㅠ_ㅠ.)

#define dubae(i) i+i
#define double(i) i*i
#define VALUE 100
#define VALUE2 VALUE+100

두개의 매크로문으로 정확히 알 수 있는데

-dubae(3); 이라 하면 함수로 착각하여 -6으로 생각이 든다. 하지만 이건 매크로다. 단순히 문장내에

정의한 매크로를 치환해주는 역할
만 해준다. 즉, -i+i; 라는 문장이 생기는 것이다.

double(3+3); 도 마찬가지로 6을 더한 후에 6*6이 될 거라고 생각하지만 아니다.

3+3 * 3+3 ; 으로 먼저 치환이 된다. 매크로문인 double을 먼저 치환해주고 연산을 해나가는 것이다.

전처리문을 보면 VALUE2가 200이라고 생각을 해버린다. 나 같은 사람은 이 순간 VALUE2는 200이라고

철썩 믿어버린다. 그럼 고정관념이 생겨서 오류가 나도 어디가 난지 죽어다 깨어나도 알지 못한다.

VALUE2*2+100 이런식으로 계산하면 500 하고 넘어가버리면 바로 버그가 탄생되는 것이다.

분명히 알아야 될 것은 매크로는 그저 문장내에 매크로에서 정의한 문장을 옮겨주는 역할만 한다.

그러므로 이 문장은 VALUE+100*2로 해석해야 정답이다. 결과값300이라는 값이 나오게 되는 것이다.

이를 방지하기 위해서 무조건 ( ) 로 묶어주는게 상책이다. -_-... 매크로로 들어간 문장이 최우선으로 계산하게

만들어 버려서 예상치 못한 실수를 원천방지 해버리자!!!

Tag |

3.20(화) 스피커음 내보기.

from Study/C언어 2007/03/20 14:00 view 25345

  왠지 예전 도스게임이 모락모락 떠오르는 음이다.. 띠~~ 띠..

#define BEEP(fre, time) Beep( (DWORD)(131*pow(1.06, fre)), (400*time));

Beep 함수를 매크로로 만들어 봤는데 -_-..  어설프네..

Beep(주파수 발생 , 발생시간) 으로 구성되어있는데 DWORD형으로 해줘야 경고 안먹는다...

131*pow(1.06, x) 이부분은 음계에 근접한 음이라고 한다. 도(0),레(2),미(4),파(5)....

Tag |