Function
함수
- C++에서의 프로그래밍 Module(모듈)이라 할 수 있다.
- 컴파일러가 제공하는 Library Function(라이브러리)과 프로그래머가 직접 정의하는 User-defined Function(사용자 정의 함수)로 구분지을 수 있다.
User-defined Function (사용자 정의 함수)
- 프로그래머가 직접 정의한 함수이다.
- Function Definition(함수 정의), Function Prototype(함수 원형), Call the Function(함수 호출)과 같은 전 과정을 프로그래머가 직접 구현해야 한다.
Parameter (매개변수)
- 호출한 함수와 호출된 함수 사이를 오가는 데이터이다.
* Formal Parameter (형식 매개변수) : 전달되는 값을 대입받는 데 쓰이는 변수를 의미하며, 이를 줄여서 "Parameter"라 한다.
* Actual Argument (실제 매개변수) : 함수에 전달되는 값 그 자체를 의미하며, 이를 줄여서 "Argument"라 한다.
ex) 함수에 매개변수를 전달하는 것은 Parameter에 Argument를 대입하는 과정이다.
- 형식 매개변수를 포함하여 함수 안에서 선언된 모든 변수는 해당 함수와 생애주기를 함께한다.
(이러한 변수들을 Local Variable(지역 변수)이라 부르며 Automatic Variable(자동 변수)라 부르기도 한다.)
- 이렇게 사용할 범위 내에서만 변수의 생애주기를 설정하는 방법은 Data Integrity(데이터 무결성)을 높여준다.
Function Definition (함수 정의)
- 리턴 데이터 타입, 함수 이름, 매개변수의 데이터형과 이름, 함수 동작 내용이 정의되어 있는 부분이다.
typeName functionName (parameterList) {
Statement(s)
return;
}
* 함수는 리턴값의 유무에 따라 두 가지로 나눌 수 있는데 특히, 리턴값이 없는 함수를 "type \(\texttt{void}\) functions"("\(\texttt{void}\)형 함수")라고 부른다.
- \(\texttt{void}\)형 함수에서는 \(\texttt{return}\) 구문을 생략할 수 있으며 \(\texttt{return}\) 구문이 없다면 함수는 닫는 중괄호에서 끝나게 된다.
- \(\texttt{return}\)형 함수는 Pascal의 "Procedures", FORTRAN의 "Subroutines", BASIC의 "Subprogram Procedures"에 해당된다.
Function Prototype (함수 원형)
- \(\texttt{main()}\) 함수가 수행되기 전에 컴파일러에게 정의된 함수의 인터페이스를 미리 알리는 역할을 한다. (에러를 줄인다.)
- 프로그램이 함수에 매개변수를 전달하는 데 실패하면, 컴파일러는 함수 원형에 근거하여 에러를 검출한다.
- 함수 원형은 하나의 구문이므로 세미콜론으로 마무리되어야 한다.
- 함수 원형 부분에서는 매개변수의 데이터형만 지정하는 것으로 충분하며 매개변수 이름까지 지정할 필요가 없다.
(그러나, 다수의 매개변수 중 같은 데이터형이 존재할 경우, 구분을 위해 이름을 제시하는 것이 프로그램 가독성에 좋다.)
- 함수 원형 부분에서 정의한 매개변수의 이름과 함수 정의 부분에서의 매개변수 이름은 서로 반드시 일치하지 않아도 된다.
- C++에서는 함수의 매개변수를 입력하는 소괄호 내부가 비어있으면 자동으로 \(\texttt{void}\) 키워드가 들어있다고 간주한다. 또한 소괄호 내부를 Ellipsis(생략부호, "...")로 채우면 매개변수 정보를 밝히는 것을 거부함을 의미한다.
(생략 부호의 사용은 \(\texttt{printf()}\) 함수와 같이 매개변수의 수가 가변적인 C 함수와의 인터페이스에만 요구되는 경향이 있다.)
#include <headerName>
...
typeName functionName (parameterList); // Function Prototype
void main() {
...
}
// Function Definition
* 함수 원형의 기능
- 컴파일러가 함수의 리턴값을 바르게 처리할 수 있게한다.
- 사용자가 정확한 개수의 매개변수, 정확한 데이터형의 매개변수를 사용했는지 컴파일러가 검사할 수 있게한다.
(사용자가 정확하지 않은 데이터형을 사용했다면, 컴파일러가 알아서 정확한 데이터형으로 변환한다.)
* Static Type Checking (정적 데이터형 검사) : 컴파일 시에 이루어지는 함수 원형 비교를 의미한다. 정적 데이터형 검사는 프로그램 실행 중에는 잡기 어려운 에러들을 컴파일 시에 잡아내는 역할을 한다.
* 컴파일러가 프로그래머로부터 함수 원형을 제공받지 않고, 직접 조사하게 될 경우의 문제점
- 컴파일러가 해당 함수를 찾기 위해 소스코드를 뒤지는 동안 \(\texttt{main()}\) 함수의 컴파일을 잠시 보류해야 한다는 점에서 컴파일 시간 측면에서 비효율이 발생한다.
- 최악의 경우, 해당 함수가 현재 소스코드가 아닌 다른 파일에 정의되어 있는 경우, 해당 소스코드를 컴파일 할 수 없게 된다. (C++에서는 하나의 프로그램을 여러 파일로 분할할 수 있으며, 이 파일들은 독립적으로 컴파일 된 후에 합쳐진다.)
- 이에 대한 유일한 해결책으로 함수를 처음 사용하는 부분보다 앞서서 함수를 정의하는 방법인데, 이 방법은 항시 가능한 방법이 아니다.
- 일반적으로 C++에서는 프로그램의 전체 구조를 \(\texttt{main()}\) 함수가 제공하기 때문에 프로그램의 맨 앞에 \(\texttt{main()}\) 함수를 작성하는 것이 관례이다.
* ANSI C와 C++에서의 함수 원형 비교
- ANSI C는 C++로부터 함수 원형의 개념을 차용했다.
- ANSI C는 클래식 C언어와의 호환성을 위해 함수 원형 사용이 선택적이나, C++에서는 함수 원형 선언이 필수적이다.
- ANSI C에서는 매개변수 정보가 담긴 소괄호 내부를 비워놓으면 매개변수 정보를 은폐하는 것을 의미하며, C++에서 이것은 \(\texttt{void}\) 키워드가 들어있음(매개변수로 아무것도 받지 않는)을 의미한다.
Return Value (리턴 값, 반환 값)
- 함수가 최종적으로 반환하는 값을 의미하며 보통 값을 반환하는 데에는 \(\texttt{return}\) 구문을 이용한다.
(조건문과 같은 구문들을 통해 함수 내에 다수의 \(\texttt{return}\) 구문을 위치시키는 것은 가능하나, 혼동을 가져올 우려가 있어 권장되지 않으며, 어떤 컴파일러는 이에 대한 경고 메세지를 출력 하기도 한다.)
- 리턴 값으로 상수나 변수 또는 일반적인 표현식이 될 수 있다.
- 배열 자체는 리턴값으로 사용될 수 없으나, 구조체나 객체의 일부로 되어 있는 배열은 리턴할 수 있다.
* C++에서는 정의된 반환 데이터형이 아닌 다른 데이터형을 반환하는 경우에는 컴파일러가 정의된 반환 데이터형으로 데이터형 변환을 수행한 다음 반환한다.
ex) 리턴형이 \(\texttt{double}\)형으로 선언되어 있는 함수에서 \(\texttt{int}\)형 데이터를 반환한다면, 그 데이터는 컴파일러에 의해 \(\texttt{double}\)형으로 변환되어 리턴된다.
+ 그러나 Function Overloading(함수 오버로딩)은 자동 변환이 이루어지지 못하게 방해한다.
+ 또한, 컴파일러가 발생 가능한 모든 경우를 자동 변환을 통해 적절히 해결하는 것은 아니다. (본래 리턴형보다 큰 데이터나 다른 카테고리의 데이터형(가령, 정수형과 구조체의 경우)일 경우에는 자동 변환이 불가능하다.)
* 함수가 값을 리턴하는 구체적인 방법
- 일반적으로 함수는 자신의 리턴값을 CPU의 지정된 레지스터나 메모리에 복사하는 방법으로 리턴한다.
- 그 과정에서 함수를 호출한 프로그램은 해당 레지스터나 메모리에 무슨 값이 있는지를 조사하는데, 그 때문에 리턴하는 함수와 호출하는 함수는 주고받을 데이터형을 일치시켜야 한다.
- Function Prototype(함수 원형)은 호출한 프로그램에게 그 위치에 어떤 타입의 데이터가 놓일지 알려주는 역할을 한다.
- Function Definition(함수 정의)은 리턴하는 함수에게 어떤 타입의 데이터를 리턴해야 하는지를 알려주는 역할을 한다.
* 중간 계산값 처리방법
- 프로그램이 복잡한 산수과정을 통해 값을 계산하는 과정에서 중간값(최종적으로 리턴하는 값이 아닌)이 매우 커지거나 작아지게 되면 의도치 않게 Underflow나 Overflow가 발생할 수 있으므로 중간값의 절댓값이 커지지 않게 계산과정을 조정해주는 작업이 필요하다.
- 중간 계산값이 적절한 범위내에 오도록, 곱셈과 나눗셈이 반복되는 과정에서는 한 번에 모든 곱셈이나 모든 나눗셈을 한꺼번에 처리하지 말고 곱셈과 나눗셈을 적절히 섞어서 중간값이 너무 커지지 않도록 처리하도록 해야한다.
(10 * 9) / (2 * 1) // 이 방법은 곱셈을 한꺼번에 처리한다. (Not good)
(10 / 2) * (9 / 1) // 이 방법은 나눗셈을 먼저 수행해서 값을 낮춘 다음 곱셈을 진행한다. (Better)
// 두 수식의 처리과정은 상이하지만, 결과값은 45로 동일하다!
함수 포인터 (Function Pointer)
- 다른 포인터들 처럼, 함수의 주소는 그 함수에 해당하는 바이너리 코드가 저장되어 있는 메모리 블록의 시작주소이다.
- 배열과 같이, 소괄호를 제외한 함수이름은 그 자체로 함수의 주솟값을 저장하고 있는 변수와 같다.
- 함수를 다른 함수의 매개변수로 전달하려면 함수 이름만 전달하면 된다.
void think(int num);
process (think); // process()에 think()의 주소를 전달한다.
process (think()); // process()에 think()의 리턴값을 전달한다.
- 함수를 지시하는 포인터를 선언할 때에도, 지시할 함수의 리턴형과 Signature(매개변수 리스트)를 명시해야 한다.
funcReturnType (*pointerName) (Signature);
// 실 사용 예시
double (*pt) (int); // 포인터 pt는 double형을 리턴하고,
// int형을 매개변수로 갖는 함수를 지목하는 포인터이다.
// 연산자 우선순위에 따른 적절한 소괄호 사용법
double (*pt) (int); // pt는 함수를 지시하는 포인터이다.
double *pt (int); // pt는 double형 포인터를 리턴하는 함수이다.
- 쉽게 말해, 함수의 원형을 먼저 선언한 다음, 함수 이름을 \(\texttt{(*ptr)}\) 형식으로 대체하면 된다.
- 함수 포인터와 함수 원형은 Signature와 리턴형이 일치해야 하며, 그렇지 않을 경우 오류가 발생한다.
+ 원칙적으로, 함수 포인터는 \(\texttt{(*ptr) (argument)}\) 와 같이, \(\texttt{(*ptr)}\)이 함수 이름을 대체하지만, \(\texttt{ptr}\)도 함수이름처럼 사용할 수 있게 허용된다.
(따라서, 함수 포인터라 할지라도 기존의 함수처럼 이용할 수 있게 되었다.)
(원칙적인 표기가 작성시에 다소 불편하더라도 함수 포인터를 사용하고 있음을 더욱 명시적으로 표현하는 좋은 방법이다.)
* C++11부터는 \(\texttt{auto}\) 키워드를 통해 간편한 작성이 가능하다.
const double* f1 (const double*, int);
const double* f2 (const double*, int);
const double* f3 (const double*, int);
const double* (*p1) (const double*, int) = f1;
auto p2 = f2; // 위 구문과 같은 기능
const double* (*pa[3]) (const double*, int) = {f1, f2, f3};
const double* px = pa[0](val, 3); // 함수 포인터 배열의 사용 예시 (val은 임의의 변수이다.)
// pa는 함수 포인터를 원소로 저장하는 배열이다.
// 여기서는 auto 키워드 사용이 불가능하다. (auto는 리스트를 초기화할 때는 사용할 수 없다.)
auto pb = pa;
// 하지만 이렇게 간접적으로 대입하는 구문은 가능하다.
- \(\texttt{auto}\) 키워드는 리스트를 초기화할 때는 사용할 수 없다.
- 함수 포인터나 배열은 표기가 복잡해보일수 있기 때문에 적절한 \(\texttt{typedef}\) 키워드를 사용했을 때 효과를 극대화할 수 있다.
Function
함수
- C++에서의 프로그래밍 Module(모듈)이라 할 수 있다.
- 컴파일러가 제공하는 Library Function(라이브러리)과 프로그래머가 직접 정의하는 User-defined Function(사용자 정의 함수)로 구분지을 수 있다.
User-defined Function (사용자 정의 함수)
- 프로그래머가 직접 정의한 함수이다.
- Function Definition(함수 정의), Function Prototype(함수 원형), Call the Function(함수 호출)과 같은 전 과정을 프로그래머가 직접 구현해야 한다.
Parameter (매개변수)
- 호출한 함수와 호출된 함수 사이를 오가는 데이터이다.
* Formal Parameter (형식 매개변수) : 전달되는 값을 대입받는 데 쓰이는 변수를 의미하며, 이를 줄여서 "Parameter"라 한다.
* Actual Argument (실제 매개변수) : 함수에 전달되는 값 그 자체를 의미하며, 이를 줄여서 "Argument"라 한다.
ex) 함수에 매개변수를 전달하는 것은 Parameter에 Argument를 대입하는 과정이다.
- 형식 매개변수를 포함하여 함수 안에서 선언된 모든 변수는 해당 함수와 생애주기를 함께한다.
(이러한 변수들을 Local Variable(지역 변수)이라 부르며 Automatic Variable(자동 변수)라 부르기도 한다.)
- 이렇게 사용할 범위 내에서만 변수의 생애주기를 설정하는 방법은 Data Integrity(데이터 무결성)을 높여준다.
Function Definition (함수 정의)
- 리턴 데이터 타입, 함수 이름, 매개변수의 데이터형과 이름, 함수 동작 내용이 정의되어 있는 부분이다.
typeName functionName (parameterList) {
Statement(s)
return;
}
* 함수는 리턴값의 유무에 따라 두 가지로 나눌 수 있는데 특히, 리턴값이 없는 함수를 "type void functions"("void형 함수")라고 부른다.
- void형 함수에서는 return 구문을 생략할 수 있으며 return 구문이 없다면 함수는 닫는 중괄호에서 끝나게 된다.
- return형 함수는 Pascal의 "Procedures", FORTRAN의 "Subroutines", BASIC의 "Subprogram Procedures"에 해당된다.
Function Prototype (함수 원형)
- main() 함수가 수행되기 전에 컴파일러에게 정의된 함수의 인터페이스를 미리 알리는 역할을 한다. (에러를 줄인다.)
- 프로그램이 함수에 매개변수를 전달하는 데 실패하면, 컴파일러는 함수 원형에 근거하여 에러를 검출한다.
- 함수 원형은 하나의 구문이므로 세미콜론으로 마무리되어야 한다.
- 함수 원형 부분에서는 매개변수의 데이터형만 지정하는 것으로 충분하며 매개변수 이름까지 지정할 필요가 없다.
(그러나, 다수의 매개변수 중 같은 데이터형이 존재할 경우, 구분을 위해 이름을 제시하는 것이 프로그램 가독성에 좋다.)
- 함수 원형 부분에서 정의한 매개변수의 이름과 함수 정의 부분에서의 매개변수 이름은 서로 반드시 일치하지 않아도 된다.
- C++에서는 함수의 매개변수를 입력하는 소괄호 내부가 비어있으면 자동으로 void 키워드가 들어있다고 간주한다. 또한 소괄호 내부를 Ellipsis(생략부호, "...")로 채우면 매개변수 정보를 밝히는 것을 거부함을 의미한다.
(생략 부호의 사용은 printf() 함수와 같이 매개변수의 수가 가변적인 C 함수와의 인터페이스에만 요구되는 경향이 있다.)
#include <headerName>
...
typeName functionName (parameterList); // Function Prototype
void main() {
...
}
// Function Definition
* 함수 원형의 기능
- 컴파일러가 함수의 리턴값을 바르게 처리할 수 있게한다.
- 사용자가 정확한 개수의 매개변수, 정확한 데이터형의 매개변수를 사용했는지 컴파일러가 검사할 수 있게한다.
(사용자가 정확하지 않은 데이터형을 사용했다면, 컴파일러가 알아서 정확한 데이터형으로 변환한다.)
* Static Type Checking (정적 데이터형 검사) : 컴파일 시에 이루어지는 함수 원형 비교를 의미한다. 정적 데이터형 검사는 프로그램 실행 중에는 잡기 어려운 에러들을 컴파일 시에 잡아내는 역할을 한다.
* 컴파일러가 프로그래머로부터 함수 원형을 제공받지 않고, 직접 조사하게 될 경우의 문제점
- 컴파일러가 해당 함수를 찾기 위해 소스코드를 뒤지는 동안 main() 함수의 컴파일을 잠시 보류해야 한다는 점에서 컴파일 시간 측면에서 비효율이 발생한다.
- 최악의 경우, 해당 함수가 현재 소스코드가 아닌 다른 파일에 정의되어 있는 경우, 해당 소스코드를 컴파일 할 수 없게 된다. (C++에서는 하나의 프로그램을 여러 파일로 분할할 수 있으며, 이 파일들은 독립적으로 컴파일 된 후에 합쳐진다.)
- 이에 대한 유일한 해결책으로 함수를 처음 사용하는 부분보다 앞서서 함수를 정의하는 방법인데, 이 방법은 항시 가능한 방법이 아니다.
- 일반적으로 C++에서는 프로그램의 전체 구조를 main() 함수가 제공하기 때문에 프로그램의 맨 앞에 main() 함수를 작성하는 것이 관례이다.
* ANSI C와 C++에서의 함수 원형 비교
- ANSI C는 C++로부터 함수 원형의 개념을 차용했다.
- ANSI C는 클래식 C언어와의 호환성을 위해 함수 원형 사용이 선택적이나, C++에서는 함수 원형 선언이 필수적이다.
- ANSI C에서는 매개변수 정보가 담긴 소괄호 내부를 비워놓으면 매개변수 정보를 은폐하는 것을 의미하며, C++에서 이것은 void 키워드가 들어있음(매개변수로 아무것도 받지 않는)을 의미한다.
Return Value (리턴 값, 반환 값)
- 함수가 최종적으로 반환하는 값을 의미하며 보통 값을 반환하는 데에는 return 구문을 이용한다.
(조건문과 같은 구문들을 통해 함수 내에 다수의 return 구문을 위치시키는 것은 가능하나, 혼동을 가져올 우려가 있어 권장되지 않으며, 어떤 컴파일러는 이에 대한 경고 메세지를 출력 하기도 한다.)
- 리턴 값으로 상수나 변수 또는 일반적인 표현식이 될 수 있다.
- 배열 자체는 리턴값으로 사용될 수 없으나, 구조체나 객체의 일부로 되어 있는 배열은 리턴할 수 있다.
* C++에서는 정의된 반환 데이터형이 아닌 다른 데이터형을 반환하는 경우에는 컴파일러가 정의된 반환 데이터형으로 데이터형 변환을 수행한 다음 반환한다.
ex) 리턴형이 double형으로 선언되어 있는 함수에서 int형 데이터를 반환한다면, 그 데이터는 컴파일러에 의해 double형으로 변환되어 리턴된다.
+ 그러나 Function Overloading(함수 오버로딩)은 자동 변환이 이루어지지 못하게 방해한다.
+ 또한, 컴파일러가 발생 가능한 모든 경우를 자동 변환을 통해 적절히 해결하는 것은 아니다. (본래 리턴형보다 큰 데이터나 다른 카테고리의 데이터형(가령, 정수형과 구조체의 경우)일 경우에는 자동 변환이 불가능하다.)
* 함수가 값을 리턴하는 구체적인 방법
- 일반적으로 함수는 자신의 리턴값을 CPU의 지정된 레지스터나 메모리에 복사하는 방법으로 리턴한다.
- 그 과정에서 함수를 호출한 프로그램은 해당 레지스터나 메모리에 무슨 값이 있는지를 조사하는데, 그 때문에 리턴하는 함수와 호출하는 함수는 주고받을 데이터형을 일치시켜야 한다.
- Function Prototype(함수 원형)은 호출한 프로그램에게 그 위치에 어떤 타입의 데이터가 놓일지 알려주는 역할을 한다.
- Function Definition(함수 정의)은 리턴하는 함수에게 어떤 타입의 데이터를 리턴해야 하는지를 알려주는 역할을 한다.
* 중간 계산값 처리방법
- 프로그램이 복잡한 산수과정을 통해 값을 계산하는 과정에서 중간값(최종적으로 리턴하는 값이 아닌)이 매우 커지거나 작아지게 되면 의도치 않게 Underflow나 Overflow가 발생할 수 있으므로 중간값의 절댓값이 커지지 않게 계산과정을 조정해주는 작업이 필요하다.
- 중간 계산값이 적절한 범위내에 오도록, 곱셈과 나눗셈이 반복되는 과정에서는 한 번에 모든 곱셈이나 모든 나눗셈을 한꺼번에 처리하지 말고 곱셈과 나눗셈을 적절히 섞어서 중간값이 너무 커지지 않도록 처리하도록 해야한다.
(10 * 9) / (2 * 1) // 이 방법은 곱셈을 한꺼번에 처리한다. (Not good)
(10 / 2) * (9 / 1) // 이 방법은 나눗셈을 먼저 수행해서 값을 낮춘 다음 곱셈을 진행한다. (Better)
// 두 수식의 처리과정은 상이하지만, 결과값은 45로 동일하다!
함수 포인터 (Function Pointer)
- 다른 포인터들 처럼, 함수의 주소는 그 함수에 해당하는 바이너리 코드가 저장되어 있는 메모리 블록의 시작주소이다.
- 배열과 같이, 소괄호를 제외한 함수이름은 그 자체로 함수의 주솟값을 저장하고 있는 변수와 같다.
- 함수를 다른 함수의 매개변수로 전달하려면 함수 이름만 전달하면 된다.
void think(int num);
process (think); // process()에 think()의 주소를 전달한다.
process (think()); // process()에 think()의 리턴값을 전달한다.
- 함수를 지시하는 포인터를 선언할 때에도, 지시할 함수의 리턴형과 Signature(매개변수 리스트)를 명시해야 한다.
funcReturnType (*pointerName) (Signature);
// 실 사용 예시
double (*pt) (int); // 포인터 pt는 double형을 리턴하고,
// int형을 매개변수로 갖는 함수를 지목하는 포인터이다.
// 연산자 우선순위에 따른 적절한 소괄호 사용법
double (*pt) (int); // pt는 함수를 지시하는 포인터이다.
double *pt (int); // pt는 double형 포인터를 리턴하는 함수이다.
- 쉽게 말해, 함수의 원형을 먼저 선언한 다음, 함수 이름을 (*ptr) 형식으로 대체하면 된다.
- 함수 포인터와 함수 원형은 Signature와 리턴형이 일치해야 하며, 그렇지 않을 경우 오류가 발생한다.
+ 원칙적으로, 함수 포인터는 (*ptr) (argument) 와 같이, (*ptr)이 함수 이름을 대체하지만, ptr도 함수이름처럼 사용할 수 있게 허용된다.
(따라서, 함수 포인터라 할지라도 기존의 함수처럼 이용할 수 있게 되었다.)
(원칙적인 표기가 작성시에 다소 불편하더라도 함수 포인터를 사용하고 있음을 더욱 명시적으로 표현하는 좋은 방법이다.)
* C++11부터는 auto 키워드를 통해 간편한 작성이 가능하다.
const double* f1 (const double*, int);
const double* f2 (const double*, int);
const double* f3 (const double*, int);
const double* (*p1) (const double*, int) = f1;
auto p2 = f2; // 위 구문과 같은 기능
const double* (*pa[3]) (const double*, int) = {f1, f2, f3};
const double* px = pa[0](val, 3); // 함수 포인터 배열의 사용 예시 (val은 임의의 변수이다.)
// pa는 함수 포인터를 원소로 저장하는 배열이다.
// 여기서는 auto 키워드 사용이 불가능하다. (auto는 리스트를 초기화할 때는 사용할 수 없다.)
auto pb = pa;
// 하지만 이렇게 간접적으로 대입하는 구문은 가능하다.
- auto 키워드는 리스트를 초기화할 때는 사용할 수 없다.
- 함수 포인터나 배열은 표기가 복잡해보일수 있기 때문에 적절한 typedef 키워드를 사용했을 때 효과를 극대화할 수 있다.