함수 (Function)
- 자료(Data)에 동작(Act)을 가하고 값을 반환하는 서브 프로그램.
<Jesse Liberty 저 Teach Yourself C++>
선언 (Declaring)
- 컴파일러에게 함수의 원형을 알려주는 것입니다.
- 정의에서는 할 수 없는 문법적 특징과 기능이 있습니다. 하나는 매개 변수의 식별자를 생략할 수 있다는 것이고, 또 하나는 매개 변수에 기본값을 지정할 수 있는 기능입니다.
함수 원형 (Prototype)
- 함수의 식별자, <호출규약>, 반환값, 매개변수로 이루어집니다.
- 형식은 다음과 같습니다:
(반환값 타입) <호출규약> (식별자)( 매개변수 타입, 매개변수 타입, ... );
식별자 (Identifier)
- 간단히 말해서 함수의 이름입니다.
- 링킹 단계에서는 함수 원형에서 파생된 별도의 식별자가 사용됩니다.
매개 변수 (Parameter, Argument)
- 해당 함수로 넘겨줄 데이터의 사본입니다.
주의할 것은 '사본'이라는 점입니다. 따라서 원본은 변경되지 않으며, 값 복사 과정이 일어나기 때문에 지나치게 크거나 많은 매개변수는 프로그램 실행 속도에 영향을 끼칠 수 있습니다.
또 하나 중요한 점은 매개 변수는 지역 변수(Local Variable)의 일종이라는 점입니다. 이것은 함수의 내부적인 동작에 대해 많은 것을 시사해주는데, 이에 대한 자세한 설명은 생략합니다.
호출 규약 (Calling Convention)
- 보통은 생략하는 것으로, 매개 변수의 처리 방법을 규정합니다. 이 경우 묵시적으로 프로젝트 기본 타입을 사용합니다. 보통 _cdecl로 지정되어 있습니다. 여기에 대한 자세한 설명은 생략합니다?
반환값 (Return Value)
- 함수를 불렀던 자 (Caller) 에게 돌려주는 값입니다. 이 값 또한 사본이며 매개 변수와 비슷한 주의점을 가지고 있습니다. 물론 매개 변수와는 반환값은 문법상 하나 뿐입니다. 어떻게 보면 매개변수와 송수신자가 뒤바뀐 것이라고 할 수 있습니다.
정의 (Definition)
- 컴파일러에게 함수가 어떻게 동작하는지를 알려주는 것입니다. 함수의 가장 중요한 요소입니다.
- 재미있는 사실은, 함수 호출에 있어서 정의는 컴파일 단계에서는 무시된다는 것입니다. 정의없이 선언만으로도 컴파일 단계까지는 문제없이 진행됩니다. 에러가 발생하는 것은 그 다음입니다.
아래가 그 예시입니다.
'error LNK2019: "void __cdecl OverLoadTest(long,long)" (?OverLoadTest@@YAXJJ@Z) 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.'
위의 '식별자' 항목에서도 설명했지만, 함수 이름은 컴파일 이후 <식별자 + 매개 변수 타입: 위의 밑줄 부분 참조>를 조합해서 링킹 단계에서 사용될 새로운 식별자를 부여받습니다. 이것은 아래에서 설명할 함수 오버로드가 가능한 이유가 됩니다.
선언만으로 컴파일이 가능하다는 이야기는, 함수 원형만으로도 호출을 위한 동작을 규정 가능하다는 것을 말해주기도 합니다.
기본 매개 변수 (Default Parameter)
- 함수 호출 때 매개 변수를 입력하지 않았을 경우, 기본적으로 지정된 값이 입력되도록 하는 것입니다. 컴파일러가 생략한 매개 변수를 대신 써준다고 생각하시면 됩니다.
- 기본 매개 변수는 자신의 뒤의 매개 변수가 없거나 (=가장 끝의 매개 변수), 기본 매개 변수를 가지고 있어야 합니다. 상식적으로 생각해서, 기본 매개 변수를 순서없이 지정할 경우, 컴파일러는 생략된 매개 변수가 어떤 것인지 판단할 수 없습니다. 그리고 어떻게 컴퓨터가 처리한다고 쳐도, 사람 또한 석달 열흘 후에는 (혹은 알콜 섭취에 따른 뇌세포 파괴로 인해 바로 다음 날이라도!) 자신이 무엇을 생략했는지 알 수 없게 될 것입니다.
가변 인자 (Variable Argument)
- printf() 를 써보신 적이 있다면 그 원형의 끝에 '...'라는 인자가 있다는 것을 기억하실 겁니다. 이것이 가변 인자입니다.
물론 넓게 해석하자면 main의 가변 인자도 있지만 일단은 저 쩜셋에 대해서만 간단히 이야기하겠습니다.
저런 괴이한 녀석이 탄생한 배경이야, prinf()에서 유추할 수도 있듯이 매개 변수의 조합이 끔찍하게 많은 경우를 염두에 둔 것입니다. 쓰면 멋있어 보이긴 하지만 다루기도 귀찮까다롭고 여러가지 문제점이 발생할 소지가 많으므로 주의를 요합니다. 즉, 오버로딩 같은 수단으로 해결할 수 있을 것 같으면 쓰지 마세요. 쓰기 전에 C++에서 지역 변수들의 메모리를 어떻게 관리하는지 이해를 할 것을 권합니다. 사실 알고나면 별 거 없는 내용이지만...
함수 중첩 (Overloading)
- 같은 이름의, 매개 변수가 다른 함수를 각각 선언 및 정의하는 것이 가능합니다. 다형성 구현에서 중요한 요소의 하나입니다. 하지만 기초용 서적에서 나오는 예제로는 이게 얼마나 의미가 있는지 느끼기 힘듭니다. 이런 기능이 필요한 곳은 실습용의 간단한 예제가 아니라 좀 더 복잡한 프로그램이기 떄문입니다. 역시 실전에서 굴러봐야 왜 기초가 중요한지 알지
실전에서는 요런 구현이 가능해집니다.
Attack( Monster* );
Attack( Player* );
Attack( Constructure* );
Attack( GM* );
Attack( Japan* );뭔가 관련없는 거라든지 공격하면 안되는 것이 있는 것 같지만 아무래도 상관없어
인라인 함수 (Inline Function)
- 최적화를 위한 방법 중 하나입니다. 인라인 함수는 동작 코드가 따로 만들어지는 것이 아니라, 호출된 자리에 바로 만들어집니다. 따라서 일반적인 함수 호출 과정이 생략됩니다. 대신 그만큼 코드 크기가 커진다는 단점이 있습니다.
인라인 함수는 선언만으로는 컴파일이 되지 않으며, 반드시 같은 소스 안에 정의가 함께해야 합니다.
인라인으로 만들기에는 지나치게 복잡한 함수의 경우, 인라인으로 지정해도 컴파일러가 무시합니다.
재귀 호출 (Recursion)
- 함수가 다시 자기 자신을 호출하는 경우를 말합니다. 보통 재귀호출은 프로그램 실행 속도에 악영향을 끼치는 편입니다. 제가 읽은 C++ 기초 서적의 예제는 피보나치 수열을 구하는 것이었는데, 재귀 호출을 사용하는 것이 코드가 간결해지기는 하지만 이 경우는 루프 문을 쓰는 것이 속도 면에서 압도적으로 좋습니다.
그러나 몇몇 경우, 재귀 호출은 간결하면서도 효과적인 코딩 수단이 되기도 합니다. 가장 대표적인 예가 트리 탐색 알고리즘입니다.
- 종종 두 세개의 메서드가 계속해서 서로를 호출하는 경우가 있습니다. 의도한 경우라도 대개 잘못된 설계이며 소싯적 루프 문을 몰랐을 때의 제 얘기입니다 의도치않는 경우라면 여러가지 심각한 문제를 유발할 가능성이 매우 높습니다.
'프로그래밍 > 참고' 카테고리의 다른 글
Call by value, adrees, reference (1) | 2013.02.05 |
---|---|
함수 기초 (0) | 2013.02.04 |
지역 변수 / 전역 변수 (0) | 2013.01.31 |
메모리 영역 (0) | 2013.01.31 |
셔플 알고리즘 (0) | 2013.01.28 |