1. 함수
// 함수
// 모듈화 -> 습관적으로 모듈화 하라. 기능에 대해 세부적으로 잘 나눠져있어야 한다.
// 하나의 함수에 여러가지 기능이 섞여있어선 안된다.
// 필요한 기능만 호출해서 바로 사용할 수 있어야 한다.
// 이 기능을 다시 조합해서 새로운 기능을 만들 수 있다.
int Add(int left, int right)
{
return left+right;
}
data = Add(10, 20); // 30이 할당된다.
// 함수
int Add(int a, int b)
{
return 0;
}
int main() // 프로그램 진입점
{
int iData = Add(100, 200);
return 0;
}
// 라이브러리 함수 사용 예시
#include <stdio.h>
printf();
printf("OUTPUT TEST");
scanf();
C언어는 프로그램 작성시 여러 함수를 만들거나 사용해 큰 흐름을 완성한다.
따라서 함수는 C언어 프로그램을 이루는 핵심 중의 핵심이라 할 수 있다.
- 함수와 반복문
함수와 반복문은 반복되는 코드를 처리하는 것과 관련되는 문법이라는 점에서 공통점이 있다.
하지만 반복문은 연속적인 반복을 의미하고, 함수는 불연속적인 반복을 통한 코드의 재사용을 의미한다.
한 프로그램 내부에서 같은 일을 수행하는 코드를 함수로 만들어 놓으면, 단지 호출 하는 것만으로도 같은 코드를 재사용할 수 있다. 또한 같은 일을 수행하는 코드의 중복을 회피할 수 있으므로 프로그램을 더욱 간결하게 만들 수 있다. - 사용자 정의 함수의 선언 및 정의
사용자 정의 함수(user-defined function)은 기존의 표준 입/출력 함수들과 달리 이미 만들어진 것이 아니라 필요에 의해 개발자 스스로 만들어낸 함수를 의미한다.
모든 C언어 프로그램에서 찾아볼 수 있는 main()함수 역시 이러한 사용자 정의 함수의 일종이다.
프로그래머는 main() 함수라는 특정 사용자 정의 함수에 내부 절차적 흐름을 정의(=기술) 하여 프로그램을 작성한다.
<함수의 선언과 정의>
함수 정의 : 함수의 내부를 이루는 구문들을 실제로 기술하는 것
함수 선언 : 반환 자료형, 함수이름, 매개변수 등을 기술하여 함수의 외형적 특성을 기술한 문법
EX. int Add(int a, int b)
<호출자와 피호출자>
호출자(caller) : 어떤 함수를 호출하는 함수
피호출자(callee) : 어떤 함수에 의해 호출되는 함수
- 매개변수
매개변수(parameter)는 호출자와 피호출자 함수를 연결해주는 매개체이다.
파라미터, 혹은 형식인수라고도 부르는데 보통 매개변수라는 말을 가장 일반적으로 사용한다.
매개변수는 자동변수로, 지역변수와 같이 스택 영역을 사용한다.
모든 호출자 함수는 피호출자 함수 매개변수의 초깃값을 '실인수'로 명시할 의무가 있다.
이때 매개변수라는 것은 말 그대로 변수를 의미하는데, 피호출자 함수 내부에 선언된 지역변수이기 때문에
아래와 같이 같은 이름을 가진 변수가 함수 몸체 스코프 안에 공존할 수 없다.
#include <stdio.h>
int Add(int a, int b)
{
int nData = 0, a; // 매개변수로 사용되는 a를 다시 재정의 하는 것은 불가능하다.
nData = a + b;
a = b = 10;
return nData;
}
- 반환 자료형
함수의 반환 자료형이란 호출자 함수가 피호출자 함수를 호출해서 얻을 수 있는 정보의 형식을 뜻한다.
이때 함수가 반환한 자료는 연산의 '임시결과'와 마찬가지로 즉시 활용하거나 저장하지 않으면 유실된다.
반환 자료를 즉시 사용한다는 것은 아래의 3가지 경우를 의미한다.
1. 자료형이 일치하는 변수에 대입하여 정보를 보관
2. 피연산자로 다른 연산에 참여
3. 다른 함수를 호출하는 데 인수로 사용
#include <stdio.h>
// 정수 셋을 매개변수로 받고 최댓값을 반환하는 함수 선언 및 정의
int GetMax(int a, int b, int c)
{
// GetMax() 함수의 지역 변수 선언 및 정의
int nMax = a;
if (b > nMax) nMax = b;
if (c > nMax) nMax = c;
return nMax;
}
int main(void)
{
int nResult = 0;
// 1. 다른 함수를 호출하는데 인수로 사용
// 함수가 반환한 값을 %d 형식으로 출력한다.
printf("MAX : %d\n", GetMax(1, 2, 3));
// 2. 피연산자로 다른 연산에 참여
// 함수가 반환한 값에 * 2 연산을 수행하고 %d 형식으로 출력한다.
printf("MAX : %d\n", GetMax(2, 3, 1) * 2);
// 3. 자료형이 일치하는 변수에 대입하여 정보를 보관
// 함수가 반환한 값을 nResult 변수에 저장한 후
// nResult에 저장한 값을 %d 형식으로 출력한다.
printf("MAX : %d\n", nResult = GetMax(3, 1, 2));
return 0;
}
- 함수 설계의 두 가지 원칙
함수를 설계할 때 다음의 두 가지 원칙을 고려하는 것이 좋다.
1. 사용자 인터페이스(UI, 겉으로 드러나는 외형)과 내부기능은 반드시 분리할 것
2. 하나의 단위 기능으로 규정할 수 있는 대상은 함수로 만들 것
EX. 평균 계산하기, 최댓값 적기, 정렬 등
- UI와 기능의 분리
프로그램의 사용자 인터페이스(UI, User Interface)는 인간과 기계가 상호작용할 수 있도록 연결되는 형식을 의미한다.
이 UI를 통해 사용자는 기계에 명령을 내리거나(입력) 기계가 생산한 정보를 인식(출력) 할 수 있다.
따라서 거의 모든 프로그램에 UI와 기능으로 나뉘어진다.
이때 이 둘은 적어도 함수단위로 구별되어야 한다.
예를 들어서 화면에 메뉴를 출력한 후 사용자로부터 메뉴 선택을 입력받아 메뉴에 대응하는 기능을 수행하는 코드가 반복되는 구조가 있다고 하면, 메뉴를 출력하고 사용자가 원하는 메뉴 선택을 받는 코드들은 PrintMenu() 함수에 구현하고 이 함수를 main() 함수에서 반복해서 호출하여 반환받은 사용자의 선택을 switch-case 문으로 분석해 메뉴에 대한 적절한 문자열을 출력할 수 있다.
이러한 반복 구조는 이벤트 루프(event loop)라고도 부르며 GUI를 가진 프로그램을 개발할 때도 적용된다.
- 재사용 가능한 단위 기능의 구현
불연속적으로 반복되거나 앞으로 다시 사용될 가능성이 높은 코드는 함수로 만드는 것이 좋다.
그래야 유지보수하기가 쉽기 때문이다.
꼭 유지보수의 용이성만이 아니라, 프로그램이 제공하는 여러 기능 각각을 하나의 함수로 만들어버리면 코드를 관리하기도 좋고 구조도 깔끔한 프로그램이 될 수 있다.
각 기능은 당장은 특정 상황에서만 필요한 기능일 수 있지만, 추후 프로그램의 다른 부분에서도 같은 기능이 필요할 가능성이 존재한다. 재사용 가능성이 없는 함수라고 해도 각 기능을 함수로 따로 떼어놓으면 소스코드에서 헤당 코드만 따로 식별하기에도 좋다.
함수 설계 원칙보다 더 큰 범위의 설계 원칙으로 DRY(Don't repeat yourself) 원칙이 있다.
이 원칙의 핵심은 "같은 일을 수행하는 코드가 중복(여러 곳에 존재)되지 않도록 하라"이다.
이는 수시로 변하는 소프트웨어의 환경적 특성에 대응해 논리적 오류를 방지하고 유지보수를 용이하게 만들기 위함이다. - 코드 분할
덩치가 너무 큰 코드는 변화에 대응하기 어렵다. 즉, 유지보수 하기가 어려워진다.
따라서 자신 스스로 너무 큰 코드라고 판단되면 일정 수준으로 크기를 줄여 여러 함수로 코드를 나누는 것이 좋다.
코드를 분할하고 연결하기 위해서는 다음과 같은 점들을 고려해야 한다.
- 함수의 이름에서 기능이 무엇인지 직관적으로 알 수 있어야 한다.
- 분할 된 코드가 들어 있는 피호출자 함수에 반드시 전달되어야 할 정보는 무엇인지 확정(매개변수)해야 한다.
- 호출자 함수는 피호출 함수를 호출하는 것으로 끝나는 것인지 아니면 반드시 어떤 정보를 반환받아야 하는지 확정(반환 자료형)해야 한다.
매개변수와 반환 자료형은 두 함수가 서로 만나는 접점이다.
따라서 호출자 함수는 피호출자 함수 매개변수의 실인수를 기술할 의무가 있으며 피호출자 함수 역시 자신의 반환 자료형에 맞는 정보를 호출자 함수에게 반환(return) 해줄 의무가 있다. 즉, 코드가 둘로 나눠졌을 때 넘겨줄 정보와 받아 낼 정보가 무엇인지 형식 수준에서 구체화 해야 한다.
따라서 매개변수와 반환 자료형을 명확히 선언할 수 있는 능력을 기르는 것이 중요하다.
좋은 코드들을 많이 접하고 경험을 늘리도록 하자. - 함수의 원형 선언
변수와 함수는 문법적으로 선언과 정의가 분리될 수 있다.
변수는 선언만으로 끝나는 경우도 있지만, 함수는 늘 선언과 정의를 분리할 수 있다.
경우에 따라서 함수의 선언이 반드시 별도로 존재해야 할 수도 있다.
이 경우 함수 정의보다 함수를 호출하는 코드가 더 먼저 등장하는 경우이다.
이럴 때는 함수의 '원형 선언(prototype)'을 소스코드 상단에 기술함으로써 컴파일러에 함수의 존재를 알려야 한다.
#include <stdio.h>
int Add(int, int);
int main()
{
// add()함수가 존재한다는 사실을 컴파일러가 알고 있으므로
// 컴파일 오류는 발생하지 않는다.
printf("%d\n", Add(3, 4));
return 0;
}
int Add(int _x, int _y)
{
return _x + _y;
}
원형 선언을 하지 않아도 피호출자 함수를 main() 함수 위로 옮기면 컴파일러는 아무런 오류도 내지 않는다.
그것이 피호출자 함수의 선언 및 정의가 되기 때문이다.
하지만 원형 선언 없이 main()함수를 피호출자보다 먼저 호출하게 되면 아직 존재하지도 않는 함수를 호출하는 것이 되기 때문에 오류가 발생한다.
이러한 링크 오류는 있어야 할 정의가 없거나, 하나만 있어야 할 정의가 여러 개 있을 때 발생한다.
2. 반복문
반복문이란 일정구간의 코드를 연속적으로 반복해 실행하는 제어문을 뜻한다.
프로그램 시작 후 메인 함수가 반환되어 프로그램이 종료되는 것을 막기 위해 대기 상태를 유지할 때 사용되는 것도 바로 이 반복문이다.
반복문을 표현하는데 있어서 고려되는 점은 아래의 세가지이다.
1. 반복을 멈추기 위한 조건
2. 반복 횟수
3. 반복 조건
가장 중요한 것은 반복을 멈추기 위한 조건식이며, 횟수와 조건에 따라서 어떤 반복문을 사용할지가 결정된다.
- while, 조건 기반 반복문
while문은 조건에 기반을 두기 때문에 if문과 문법 구조가 동일하다.
괄호 속에 조건식을 기술하는 문법이나 여러 구문을 스코프로 묶는 것도 동일하다.
하지만 while문은 내부 구문이 끝나면 다음으로 넘어가는 if문과 달리,
내부 구문의 종료와 동시에 다시 구문의 처음으로 돌아가 조건식을 확인하는 것을
조건식이 불성립할 때까지 반복하는 반복문이라는 점에서 차이가 있다.
그렇기 때문에 조건이 참이면 계속해서 구문을 실행하고, 반복 하던 와중에 조건식의 결과가 거짓이 되면 그때 반복을 끝내게 된다. - 무한루프
반복문을 멈추기 위한 조건식에 문제가 있어 반복이 끝나지 않는 문제를 무한루프라고 한다.
반복문 내부 연산으로 반복문을 끝내는(혹은 탈출하는) 상태에 도달하지 않으면
계속해서 똑같은 동작을 반복하게 되므로 프로그램이 죽어버리는 치명적인 오류가 발생할 수 있다. - 반복문 내부에 선언한 자동변수
반복문 내부에 변수를 선언하는 것은 바람직하지 않다.
지역 변수의 경우 스코프가 닫히면 그 내부에 선언 및 정의한 변수가 사라진다.
그러므로 논리적 오류를 야기할 가능성이 높고, 불필요한 생성과 소멸을 반복하는 효율상의 문제도 발생한다.
따라서 문제가 발생할 경우 스택영역 관리 방법에 대한 이해 없이는 해결할 수가 없으며 매우 비효율적이다. - for, 계수 기반 반복문
for문과 while문은 반복문이라는 점에서 같지만,
while문은 조건에 기반을 둔 측면이 강하고 for문은 계수에 기반을 둔 측면이 강하다.
for문 처럼 계수에 기반을 둔 반복을 while문으로 기술할 경우, 반복 자체를 위한 코드가 세 부분임에도 문법상으로는 조건식만 기술하면 된다.
때문에 중첩된 반복문을 구현할 때 계수기 초기화 코드를 빼먹거나,
반복문 내부에 반드시 기술되어야 하는 계수기 증가식을 깜빡하는 실수가 벌어질 가능성이 있다.
하지만 for문은 그 세 가지 요소를 모두 한 행에서 기술하도록 강제하는 문법이다.
반복횟수에 영향을 주는 계수기 초기화, 조건식, 계수기 증감 등의 코드를 모두 한 행에서 확인할 수 있다.
따라서 단 한 줄의 코드만으로도 for문을 몇 회 반복 수행하는지 명확하게 알 수 있다.
// 계수기 초기화; 조건식; 계수기 증가
for (i = 0; i < 5; ++i)
{
printf("%dth\n", i);
}
조건이 참일 때 반복문 내부를 실행한 뒤 다시 올라올 때는 계수기 증가 코드 ++i를 실행한 후 i < 5 조건을 비교한다.
이때 원한다면 for문의 오른쪽 괄호 내부 구문 중 일부를 생략할 수도 있다.
예를 들어 for( ; i < 5; ++i)와 같이 초기 수행식을 생략하거나 for( i = 0; i < 5; ) 처럼 계수기 증가 부분을 생략하거나, 심지어 조건식도 생략할 수 있다.
for( ; ; )의 경우 while(1)과 같은 의미이다. (무한 반복)
- do while문
do while문은 while문이나 for문과 달리 반복대상 단위코드를 조건과 상관없이 일단 한 번 실행한 후 조건을 비교한다.
따라서 반복 대상 코드가 적어도 한 번은 반드시 실행되며, 자주 쓰이진 않지만 이런 특성이 반드시 필요한 반복을 구현하는데 적합하다.
예를 들어, 사용자가 유효범위 값을 입력하지 않았을 때 다시 입력하는 코드를 작성하고자 할 때 do while문을 사용해 일단 입력을 받은 뒤 유효범위를 체크하고 넘어가면 올바른 값을 선택하지 않을 시 계속해서 입력을 해야하므로 간편하게 구현할 수 있다.
이런식으로 사용자가 프로그램을 잘못 사용할 가능성 혹은 실수의 가능성 등을 봉쇄하는 기능은 프로그램의 설계상 아주 중요하다.
do while문의 문법적인 특이점은 조건식 괄호 뒤에 세미콜론(;)이 붙는다는 점이다.
이는 반복문 중에 유일하게 do while만 가진 특징이다.
#include <stdio.h>
int main(void)
{
char ch = 0;
do
{
// 조건을 나중에 비교하므로
// 일단 한 번은 무조건 실행한다.
ch = getchar();
putchar(ch);
// 조건식 오른쪽 끝에 ;이 있다는 점에 주의한다.
} while (ch != '\n');
return 0;
}
- break과 continue
break는 switch-case문이나 반복문(while, for, do while)문에서 사용되어 프로그램의 흐름 내부를 벗어나게 하는 탈출을 위한 제어문이다.
continue문은 반복문 내부에만 사용할 수 있는 구문이며, 수행 즉시 나머지 구문들을 생략하고 다시 조건식을 비교하는 제어문이다. 만약 제어문이 참일 경우 반복문을 계속 수행한다.
이 두 제어문이 사용되는 가장 흔한 이유는 반복문 내부에서 수행한 어떤 연산에서 예외가 발생했을 때 이에 대응하기 위함이다.
일반적으로 break문과 continue문은 단독으로 사용되기 보다, if문과 결합하여 특정 조건일 때 작동하게끔 코드를 작성하는 것이 대부분이다.
+)
// <반복문>
// 프로그램 시작 후 메인 함수가 끝나는 것을 방지하기 위해서
// 대기 상태를 유지 할 때 반복문을 사용한다.
// 1. for()
// for(횟수체크를 위한 변수(반복자)를 초기화; 반복자 조건 체크 EX. (A<B);반복자 변경 EX.++,--) {}
for(int i = 0; i < 10; ++i) // 0~9까지 돌면서 반복
{
// 조건 체크 결과 참일 경우 이 영역 안의 코드가 실행 된다.
printf("OUTPUT TEST");
}
// 2. while
int i = 0;
while(i < 2)
{
printf("OUTPUT TEST\n");
++i;
}
// <반복문 관련 그 외 문법>
// 1. Continue; -> 반복 구문 수행 중, 컨티뉴를 만나면
// 이후 작성된 뒷 부분을 수행하지 않고 바로 조건체크 부분으로 넘어간다.
// 2. break
// break문을 만나면 즉시 반복문을 빠져나간다.
for(int i = 0; i < 4; ++i)
{
if(i % 2 == 1) // i가 홀수이면
{
continue; // 출력하지 않고 바로 조건 체크로 넘어감
}
printf("OUTPUT TEST\n"); // i가 짝수면 정상 출력
}
'C,C++ > [강의] 어소트락 C++' 카테고리의 다른 글
함수와 재귀함수 (0) | 2022.11.28 |
---|---|
VS 단축키 및 편의사항 (0) | 2022.11.25 |
define / 비트연산자 (0) | 2022.11.23 |
주석, 변수, 자료형, 연산자 (0) | 2022.11.22 |
C++을 쓰는 이유 (0) | 2022.11.22 |