A. 함수
1. 함수 개요
a. 함수란?
🌟 하나의 특별한 목적의 작업을 수행하기 위해 독립적으로 설계된 프로그램 코드의 집합
b. 함수의 종류
1) 표준 함수
2) 사용자 정의 함수
c. 함수를 사용하는 이유
1) 반복적인 프로그래밍을 피할 수 있기 때문
2) 모듈화로 가독성이 좋아짐
3) 손쉽게 유지보수가 가능
d. 함수의 규칙
함수의 규칙정확히 명시된 규칙은 없지만, 하나의 기능을 하나의 함수로 만들어야 함
e. 함수의 정의
🤍 문법 설명
✔ 반환 자료형 : 함수가 모든 작업을 마치고 반환하는 데이터의 타입을 명시
✔ 함수 이름 : 함수를 호출하기 위한 이름을 명시
✔ 매개변수 목록(parameters) : 함수 호출 시에 전달되는 인수의 값을 저장할 변수들을 명시
함수의 인수(매개변수)는 0 ~ n 개 가능
✔ 함수 몸체 : 함수의 고유 기능을 수행하는 명령문의 집합
리턴값은 0 or 1개만 가능
f. 함수의 원형 선언
🌟 함수가 나중에 정의되어있다고 알리기 위해 미리 선언하는 것
💙 미리 선언해야 하는 이유
컴파일 방식에는 두가지가 있음
* 단일 패스 컴파일 방식 : 차례대로 한 번에 컴파일 하는 방식
=> 원형 선언 ⭕ (주로 오래된 컴파일러)
* 다중 패스 컴파일 방식 : 여러 번에 걸쳐 컴파일 하는 방식
=> 원형 선언 ❌ (최신)
주로 오래된 컴파일러는 단일패스(1) 방식을 사용하므로 C 표준에서는 원형 선언을 필요로 함
👉 단일 패스 컴파일 방식이 오류를 발생시키는 이유
1. C 컴파일러는 main() 함수를 먼저 호출함
2. main() 함수에 등장하는 함수를 아직 알지 못함
3. 컴파일 오류를 생성
📘 문법
반환타입 함수이름(매개변수타입);
🤓 예제 : 함수의 원형 선언을 추가
#include <iostream>
using namespace std;
int SmallNum(int, int);
int main(void)
{
int result;
result = SmallNum(4, 6);
cout << " 두 수 중 더 작은 수는 " << result << "입니다." << endl;
result = SmallNum(8, 6);
cout << " 두 수 중 더 작은 수는 " << result << "입니다." << endl;
result = SmallNum(2, 8);
cout << " 두 수 중 더 작은 수는 " << result << "입니다." << endl;
return 0;
}
int SmallNum(int num1, int num2)
{
if (num1 <= num2)
{
return num1;
}
else
{
return num2;
}
}
✨ 실행결과
두 수 중 더 작은 수는 4입니다.
두 수 중 더 작은 수는 6입니다.
두 수 중 더 작은 수는 2입니다.
g. main() 함수
🌟 프로그램이 실행되면 제일 먼저 자동으로 호출되는 함수
main() 함수도 함수이기 때문에 인수를 전달받을 수도 있고, 반환값을 가질 수 있음
📘 문법
void(또는 int) main(int argc,char *argv[]);
//int형 변수 argc : 인수로 전달되는 문자열의 개수를 명시
//char형 포인터의 포인터인 argv : 인수로 전달된 각각의 문자열이 포함되는 배열
2. 인수 전달 방법
함수에 필요한 데이터를 인수(argument)로 전달해 줄 수 있는 두가지 방법이 있음
a. 값에 의한 전달(call by value)
인수로 변수 값을 함수 내의 매개변수에 복사하는 방식
복사된 값이된 매개변수는 인수로 전달된 변수와는 완전히 별개의 변수 (영향 ❌ )
👉 원래 알고있는 전달방식
b. 참조에 의한 전달(call by reference)
인수로 변수의 주소값을 전달
즉 함수의 매개변수에 인수로 전달된 변수의 원래 주소값을 저장
👉 인수로 전달된 변수의 값을 함수 내에서 변경가능
🤓 예제
#include <iostream>
using namespace std;
void Local(int&);
int main(void)
{
int var = 10;
cout << "변수 var의 초기값은 " << var << "입니다." << endl;
Local(var);
cout << "Local() 함수 호출 후 변수 var의 값은 " << var << "입니다.";
return 0;
}
void Local(int& num)
{
num += 10;
}
✨ 실행결과
변수 var의 초기값은 10입니다.
Local() 함수 호출 후 변수 var의 값은 20입니다.
3. 재귀 호출(recursive call)
a. 재귀호출이란?
🌟 함수 내부에서 함수가 자기 자신을 또다시 호출하는 행위
재귀 호출은 알고리즘이나 자료 구조론에서는 매우 중요한 개념 중 하나
자기가 자신을 계속해서 호출 -> 무한반복을 피하기 위해 중단될 명령문이 반드시 포함되어야 함
b. 재귀호출의 장단점
👍 장점
* 직관적인 프로그래밍을 가능하게 함
함수는 그냥 봐서는 그 목적을 바로 알 수 없으며, 코드를 해석해야 무슨 목적으로 만든 함수인지 판단 가능함
하지만 재귀 호출은 복잡한 문제도 매우 간단하게 논리적으로 접근해 표현 가능함
👎 단점
* 비재귀 호출보다 실행시간이 오래걸림 (성능 상의 문제)
* 무한 재귀호출의 위험성
c. 재귀호출의 작성 방법
재귀 알고리즘을 구상 -> 의사 코드 작성 -> 구현
🧡 의사 코드(pseudo code)
특정 프로그래밍 언어의 문법에 맞춰 작성된 것이 아닌, 일반적인 언어로 알고리즘을 표현한 코드
d. 재귀 호출의 예시
1) 먼저 1부터 n까지의 합을 구하는 함수를 만들기
//한번에 왜 정의되어있는지 알기 어려움
int sum(int n) {
int i;
int result = 0;
for (i = 1; i <= n; i++)
{
result += i;
}
return result;
}
함수를 해석해야지만 무슨 목적으로 만든 함수인지 알 수 있음
2) 위의 함수를 보고 의사코드 작성하기
시작
1. n이 1이 아니면, 1부터 (n-1)까지의 합에 n을 더한 값을 반환함.
2. n이 1이면, 그냥 1을 반환함.
끝
3) 재귀 호출을 사용해 구현하기
int rSum(int n)
{
if (n == 1) // n이 1이면, 그냥 1을 반환함
{
return 1;
}
return n + rSum(n-1); // n이 1이 아니면, n을 1부터 (n-1)까지의 합과 더한 값을 반환함.
}
👉 여기서 if문이 없다면 스택 오버플로우가 발생 (매개 변수이기 때문)
4. 함수 포인터
a. 함수 포인터(function pointer) 란?
프로그램에서 정의된 함수 => 실행될 때 모두 메인메모리에 올라감
이때 함수의 이름 = 메모리에 올라간 함수의 시작주소를 가리키는 포인터 상수가 됨
🌟 즉, 함수의 시작 주소를 가리키는 포인터 상수를 함수 포인터라고 함
포인터 타입은 함수의 반환값과 매개변수에 의해 결정
= 함수의 원형을 알아야 함수 포인터를 만들 수 있음
📘 문법
void func (int, int); //함수 원형
void (*ptr_func) (int, int); //함수 포인터
//함수 포인터 사용시 연산자의 우선순위 때문에 반드시 *ptr_func 부분을 괄호()로 둘러싸야 함
❔ 예제 : 함수 포인터는 함수를 또 다른 함수의 인수로 전달할 때 유용
#include <iostream>
using namespace std;
double Add(double, double);
double Sub(double, double);
double Mul(double, double);
double Div(double, double);
double Calculator(double , double, double (*func)(double, double));
int main(void)
{
double (*calc)(double, double) = NULL; // 함수 포인터 선언
double num1 = 3, num2 = 4, result = 0;
char oper = '*';
switch (oper)
{
case '+' :
calc = Add;
break;
case '-':
calc = Sub;
break;
case '*':
calc = Mul;
break;
case '/':
calc = Div;
break;
default:
cout << "사칙연산(+, -, *, /)만을 지원합니다.";
break;
}
result = Calculator(num1, num2, calc);
cout << "사칙 연산의 결과는 " << result << "입니다.";
return 0;
}
double Add(double num1, double num2)
{
return num1 + num2;
}
double Sub(double num1, double num2)
{
return num1 - num2;
}
double Mul(double num1, double num2)
{
return num1 * num2;
}
double Div(double num1, double num2)
{
return num1 / num2;
}
double Calculator(double num1, double num2, double (*func)(double, double))
{
return func(num1, num2);
}
✨ 실행결과
사칙 연산의 결과는 12입니다.
b. 함수 포인터의 표기법
위에서 사용한 함수 포인터는 아래와 같은데
double (*calc)(double, double) = NULL;
이게 표기법이 복잡하다는 단점이 있음
따라서 typedef , auto (C++11부터 제공) 두 가지 키워드를 제공함
1) typedef 키워드
복잡한 함수 포인터형에 새로운 이름을 붙일 수 있음
📘 함수 포인터의 선언
typedef double (*CalcFunc)(double, double); // 함수 포인터에 calcFunc이라는 새로운 이름을 붙임.
CalcFunc ptr_func = calc;
❔ 앞선 예제에 typedef 키워드를 사용하여 간략화한 예제
#include <iostream>
using namespace std;
typedef double (*Arith)(double, double); // typedef 키워드를 이용한 새로운 이름 선언
double Add(double, double);
double Sub(double, double);
double Mul(double, double);
double Div(double, double);
double Calculator(double , double, Arith);
int main(void)
{
Arith calc = NULL; // 함수 포인터 선언
double num1 = 4, num2 = 5, result = 0;
char oper = '+';
switch (oper)
{
case '+' :
calc = Add;
break;
case '-':
calc = Sub;
break;
case '*':
calc = Mul;
break;
case '/':
calc = Div;
break;
default:
cout << "사칙연산(+, -, *, /)만을 지원합니다.";
}
result = Calculator(num1, num2, calc);
cout << "사칙 연산의 결과는 " << result << "입니다.";
return 0;
}
double Add(double num1, double num2)
{
return num1 + num2;
}
double Sub(double num1, double num2)
{
return num1 - num2;
}
double Mul(double num1, double num2)
{
return num1 * num2;
}
double Div(double num1, double num2)
{
return num1 / num2;
}
double Calculator(double num1, double num2, Arith func)
{
return func(num1, num2);
}
✨ 실행결과
사칙 연산의 결과는 9입니다.
2) auto 키워드
📘 문법
auto ptr_func = calc;
출처 : http://www.tcpschool.com/cpp/cpp_function_basic
'프로그래밍 언어 > C++' 카테고리의 다른 글
[게임 프로그래머 입문 올인원] 포인터와 배열 : 배열 기초 (18강) (0) | 2023.09.06 |
---|---|
[자료형(data type)] C++ 파생형 함수 총정리 2 (0) | 2023.09.06 |
[게임 프로그래머 입문 올인원] 함수와 디버깅 : 파일 분할 (16강) (0) | 2023.09.06 |
[게임 프로그래머 입문 올인원] 함수와 디버깅 : 스택과 레지스터 (13강) (0) | 2023.09.05 |
[게임 프로그래머 입문 올인원] 함수와 디버깅 : 함수, 변수의 범위 (12강) (0) | 2023.09.05 |