소년코딩

01.09 - 전방 선언과 정의 (forward declarations and definitions)

add.cpp 라는 샘플 프로그램을 보자.

#include <iostream>

int main()
{
    std::cout << "The sum of 3 and 4 is: " << add(3, 4) << std::endl;
    return 0;
}

int add(int x, int y)
{
    return x + y;
}
아마도 이렇게 예상할 것이다.
The sum of 3 and 4 is: 7

그러나 비쥬얼 스튜디오에서는 아래와 같은 컴파일 에러가 발생한다. (2015 기준)

add.cpp(5) : error C3861: 'add': identifier not found

위 프로그램이 컴파일되지 않는 이유는 컴파일러가 파일의 소스코드를 순차적으로 읽기 때문이다. 컴파일러가 main() 함수의 5번째 행에서 add() 함수 호출을 하면 9번째 행까지 add() 함수를 정의하지 않았으므로 add가 무엇인지 알 수 없다. 그러면 '식별자 찾을 수 없음'과 같은 에러가 발생한다.

#include <iostream>

int add(int x, int y)
{
    return x + y;
}

int main()
{
    std::cout << "The sum of 3 and 4 is: " << add(3, 4) << std::endl;
    return 0;
}

위와 같은 방법으로 main() 함수가 add() 함수를 호출할 때, 컴파일러는 add() 함수가 무엇인지 알 수 있다. 위 프로그램은 매우 간단하므로 변경이 쉽다. 그러나 크고 복잡한 프로그램에서는 어떤 함수가 다른 함수를 어떤 순서로 호출하는지 파악하는 것은 매우 복잡하다.


전방 선언(Forward Declaration)

전방 선언(forward declaration)은 실제로 식별자를 정의하기 전제 식별자의 존재를 컴파일러에 미리 알리는 것이다.

함수의 경우, 함수의 몸체를 정의하기 전에 함수의 존재에 대해 컴파일러에 미리 알리는 것을 말한다. 이 방법으로 컴파일러가 함수를 호출할 때 함수가 정의된 방법이나 위치를 아직 모를 때에도 함수를 호출한다는 것을 이해시킬 수 있다.

함수의 전방 선언을 하려면 함수 원형(prototype)이라고 하는 선언문(declaration statement)을 사용해야 한다. 함수 원형은 리턴 타입, 이름 그리고 매개 변수로 구성되지만, 함수 몸체({} 사이의 부분)는 포함하지 않는다. 함수 원형은 명령문(statement)이므로 세미콜론(;)으로 끝나야 한다.

다음은 add() 함수의 함수 원형(function prototype)이다.
int add(int x, int y); // 함수 원형은 리턴 타입, 이름, 매개 변수로 구성되어 있다.
# 함수 원형을 사용해서 이전에 컴파일 되지 않은 프로그램을 수정해보자.
#include <iostream>

int add(int x, int y); // add() 함수의 전방 선언(함수 원형)

int main()
{
    std::cout << "The sum of 3 and 4 is: " << add(3, 4) << std::endl;
    return 0;
}

int add(int x, int y)
{
    return x + y;
}

// The sum of 3 and 4 is: 7

이제 컴파일러는 불평불만을 하지 않고 컴파일을 성공적으로 수행한다.

또한, 함수 원형에서 매개 변수의 이름을 지정하지 않고 선언할 수 있다.

int add(int, int); // 매개변수의 이름을 생략 가능하다.

그러나 함수 원형으로 실제 함수 매개 변수가 무엇인지 이해를 하기 위해 같은 이름의 매개 변수를 지정하는 것이 좋다.


선언 vs 정의 (Declaration vs Definition)

C++ 에서는 종종 선언(declaration)정의(definition)이라는 용어를 사용한다. 두 가지의 차이점을 알아보자.

*정의(definition)는 식별자(identifier)를 실제로 구현하거나 인스턴스화(메모리 할당) 한다. *

int add(int x, int y) // add() 함수를 구현한다.
{
    return x + y;
}

int x; // x라는 정수형 변수를 인스턴스화(메모리 할당)

링커(Linker)를 만족하게 하기 위해서는 정의가 필요하다. 정의하지 않고 식별자를 사용하면 링커에서 에러가 발생한다.

선언(declaration)은 식별자(변수 또는 함수 이름) 및 해당 타입의 존재를 컴파일러에 알려주는 명령문(statement)이다.

// 컴파일러에게 두 개의 int 매개 변수를 사용하고 int를 반환하는 "add"라는 이름의 함수를 알려준다.
int add(int x, int y);

int x; // x라는 정수형 변수를 컴파일러에게 알려준다.

선언하지 않고 식별자를 사용하면 컴파일러에서 오류가 발생한다.

int x; 가 선언과 정의 모두에서 나타난다. C++ 에서 모든 정의는 선언으로도 간주한다. int x; 는 정의이기 때문에 기본적으로 선언이다. 따라서 대부분은 정의만 필요하다. 그러나 정의되기 전에 식별자를 사용해야 한다면 명시적인 선언을 해야 한다.


cpp 번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/17-forward-declarations/ 입니다.

댓글 로드 중…

블로그 정보

소년코딩 - 소년코딩

소년코딩, 자바스크립트, C++, 물리, 게임 코딩 이야기

최근에 게시된 이야기