-
C 언어 프로그래밍은 main 함수에 사용자가 원하는 작업을 절차상의 흐름에 맞게 작성한 것이라고 볼 수 있다.
즉 C 언어는 함수로 시작해서 함수로 끝난다. 따라서 함수에 대한 이해가 필요하다.
함수는 다음과 같은 구조로 생겼는데 최상단의 함수의 원형 그리고 '{}' 안에 들어가는 정의의 조합으로 함수가 동작하게 된다. 이때 함수의 선언 부를 보면 다음과 같다
1. 함수가 반환할 자료형
2. 함수의 이름 ( 내부적으로는 해당 함수의 주소를 나타낸다 )
3. 함수를 실행함에 있어 받아 올 자료 ( 매개변수, 파라미터, 인자 등으로 불림)
+ 함수의 원형에 기술하는 매개변수는 반드시 자료형을 전부 기술해 주어야 한다.
+ 함수의 매개변수는 지역변수 + 자동 변수의 개념이다.
+ 따라서 매개변수의 유효 범위는 해당 함수의 스코프에 국한된다. ( 즉 매개 변수와 중복된 변수명으로 선언은 불가하다. )
매개 변수의 유효성 검사
함수를 작성함에 있어 매개 변수의 유효성 검사는 피호출자 함수가 하는 것이 맞다. 다음 예를 보자
A라는 호출자가 있고 B라는 피호 출자가 있다고 가정했을 때 A는 B를 호출할 것이다. 이때 B 함수의 기능은 정수 10을 넘어갈 시 동작하지 않는다고 봤을 때 과연 호출자가 매개변수의 유효성을 검사하는 것이 맞는 것 일까?
A가 유효성 검사를 할 경우 호출 할 때마다 유효성 검사를 해야 할 것이다. 따라서 B함수에서 오류처리에 대한 구문을 작성하여 해당 함수의 매개변수에 대한 유효성 검사를 실시해야 한다.
Ex )
n의 매개변수의 값이 10 이하의 정수여야 할 경우
if n > 10
retrun 0 ;
위와 같은 구문을 통해 10이 넘는 값이 매개변수로 들어올 경우 0을 반환하는 것을 통해 매개변수가 유효하지 않다고 알림 즉 유효성 검사의 구문을 피호출자 함수 내부에 작성하여 호출자 함수가 유효하지 않은 값을 넣어도 0 반환을 통해 유효하지 않은 매개변수 라고 알 수 있다.
두 가지 함수 설계 원칙
자 이제 위에서 설명한 내용을 토대로 함수라는 것이 어떻게 생겼고 어떤 구조 인지는 어느 정도 알 것이다. 그렇다면 이런 의문이 생길 것이다. 도대체 어느 코드를 함수로 만들지? 함수는 어떨 때 선언해서 사용하지?
C 언어를 사용하는 프로그래머는 위와 같은 의문을 항상 품으며 코드를 작성해야 한다. 또한 위와 같은 의문에 대해 적당한 해결책을 제시해주는 것을 함수 설계 원칙이라 칭한다. 아래에 함수 설계 원칙에 대해 기술
1. UI ( 사용자 인터페이스 )와 기능의 분리
사용자 인터페이스 즉 프로그램이 사용자로부터 글자 or 숫자 등의 정보를 입력받거나 출력하는 부분과 프로그램이 실질 적으로 사용자의 입력에 대해서 연산을 하는 부분은 함수로써 구분해 주어야 할 필요가 있다.
물론 같이 구현하여 프로그램을 작성할 수 있지만 그렇게 된다면 각 기능들에 대한 의존성이 높아 짐에 따라 추후 유지보수를 함에 있어 불편함이 발생한다. 따라서 함수라는 분리를 통해 기능을 나누고 프로그램이 동작함에 필요한 각 기능들의 의존성을 낮추어 프로그램의 유지보수를 더 효율적으로 할 수 있게끔 프로그램을 작성해야 한다.
Ex )
나이를 입력받아 요금을 계산하는 경우
나이를 입력 ( scanf ) 받는 부분은 UI의 영역에 들어감
요금을 계산 ( Look up table ) 하는 부분은 기능의 영역에 들어감
따라서 따로 분리하여 함수를 작성하여 서로 상호 의존성을 낮추어 나이가 바뀔 경우 UI 함수만 수정하여 프로그램을 유지보수하는 측면으로 프로그램을 작성해야 한다. 아래 코드 참조
#include <stdio.h> // 기능적 요소를 담은 함수 int add(int param){ int res = 1, i = 0; // 유효성 검사 구문 0을 반환하여 사용자에게 알려줌 if (param < 1 || param > 10) return 0; // 실질적 계승구하기 기능 구문 for (i = 0; i <= param; ++i) res *= i; return res; } // 사용자에게 입력을 받는 함수 int get_input(void){ int res = 0; scanf_s("%d", &res); return res; } // 출력에 관한 내용 int main(void){ int n_input = 0, res = 0; printf("정수를 입력하시오 : "); // get_input 호출 n_input = get_input(); // add 호출 res = add(n_input); // add에서 유효성 검사 결과 0일 경우 error 메세지 출력 // 아닐 경우 바른 값 출력 if (res == 0) printf("바른 값을 입력 \n"); printf("바른 값은 %d \n", res); }
위와 같이 코드를 UI 부분과 실질적 기능적 부분을 나누어 프로그램을 작성할 수 있다.
양이 적을 경우에는 복잡해 보이지만 규모가 커질 경우 위와 같이 나누어서 관리하는 게 유지보수에 더욱 도움이 됨 따라서 프로그램을 작성할 때 UI와 기능에 대해서 분리하여 프로그램을 작성한다. ( 함수가 많아져서 하는 걱정을 괜찮다. )
2. 재사용 가능한 단위 기능의 구형
즉 최솟값 구하기, 최댓값 구하기, 내림차 정렬, 오름차 정렬과 같이 일정한 패턴을 가지면서 계속해서 재활용하여 사용할 수 있는 코드 또는 자주 등장하는 코드는 해당 코드를 함수로 묶어서 기능을 구현해야 한다는 의미이다.
추가적으로 함수 설계 원칙보다 더 큰 범위의 설계 원칙으로 DRY 원칙이 있다. 이 DRY 원칙의 핵심은 " 같은 일을 수행하는 코드가 중복 (여러 곳에 등장)되지 않도록 하라! "이다.
예를 들어 요금 할인을 하는 코드가 프로그램 내부에 3군데 존재할 경우 할인 폭이 변경될 경우 작성자는 3군데 모두 변경을 해야 하며 만약 변경하지 못한 곳이 발생할 경우 바로 버그로 이어진다. 하지만 요금 할인에 대한 코드를 함수로 구현하여 필요할 때마다 호출해서 사용한다면 함수만 변경해서 사용할 수 있다.
전역 변수
1. 전역 변수란?
위에서 함수의 매개변수는 자동 변수이며, 지역변수라고 말한 적이 있다.
자동 변수 : 스코프가 끝나면 메모리에서 사라짐
지역 변수 : 변수가 속한 스코프 안에서 유효하다.
위와 같은 특성을 가진 변수가 자동, 지역 변수이다. 그렇다면 전역 변수란 무엇인가?
이름 그대로 전역 즉 해당 프로그램의 모든 영역에서 유효한 변수라는 의미이다. 따라서 모든 영역에서 유효한 만큼 만약 잘못 사용한다면 서로 독립적이어야 할 두 코드를 하나로 묶어주는 역할을 수행하는 특성으로 인해 설계적으로 큰 오류가 발생할 수 있다.
위에서 UI와 기능을 함수로 나누어 설계한 이유는 함수를 통해 서로 독립적인 코드를 만들어 만약 문제가 발생할 경우 한 곳만 수정 보완하여 유지보수를 쉽게 하기 위함이다. 이때 전역 변수를 오남용 한다면 서로 독립적이어야 할 코드를 전역 변수라는 매개로 묶어버려 의존성을 만들어 버림으로 인해 불필요한 의존관계가 생긴다. 따라서 전역 변수는 최대한 사용을 자제해야 한다.
2. 전역 변수와 식별자 검색 순서
- 가장 최근에 형성된 블록 스코프에 속한 지역변수
- 현재 블록 스코프의 외부를 감싸고 있는 상위 스코프 ( 최대 함수 몸체까지 검색 )
- 파일 스코프 ( 전역변수 )
프로그램 기초 설계 원칙
사실 좋은 설계라는 것은 간단하다. 프로그램을 적절한 단위 요소로 나누고 각 요소가 다른 요소의 변화에 영향을 받지 않도록 의존성을 최대한 낮추는 것이다.
따라서 몇 가지 설계를 위한 원칙이 있으며 내용은 다음과 같다.
1. 같은 일을 수행하는 코드가 중복 (여러 곳에 존재) 되지 않도록 작성해야 한다. ( 따라서 같은 일을 수행하는 코드는 함수로 작성하여 필요할 때마다 호출하는 방식으로 사용 )
2. 함수의 이름은 해당 기능을 유추할 수 있도록 명확하게 선언한다.
3. 함수를 작성한 뒤 해당 함수의 매개변수 유효성 검사는 해당함수 본인이 한다. 즉 매개변수 유효성 검사는 피호 출자 함수가 수행한다.
4. UI (사용자의 입력, 사용자에게 출력)와 기능은 서로 독립적으로 작성한다. ( 즉 함수로 구현하여 서로 독립적이게 작성 )
5. 만약 자주 사용하거나 재사용할 수 있는 코드는 함수로 작성하여 재사용한다.
6. 전역 변수의 사용은 최대한 자제하며 만약 사용해야만 할 경우 전역 변수인 걸 바로 식별할 수 있도록 이름은 특별하게 선언한다.
'C언어' 카테고리의 다른 글
가상메모리 (0) 2020.03.03 메모리의 구조와 포인터 (0) 2020.03.01 배열의 주소와 이름의 관계 (0) 2020.02.26 프로그램의 성능향상 ( 쇼트서킷 ) (0) 2020.02.24 연산자 (sizeof), 디스어셈블 (0) 2020.02.24