전방 선언
헤더 파일을 만들지 않고, include를 하지 않더라도 다음과 같이 전방 선언해서 다른 cpp 파일의 함수를 사용할 수 있다. 컴파일러는 main.cpp를 컴파일 할 때, 전방 선언된 함수를 보고 어딘가에 `add(int a, int b)` 함수의 정의가 존재할거라고 믿고 컴파일에 문제가 생기지 않는다. 컴파일러는 컴파일만하고, 그 뒤의 작업인 실행할 때 링킹해서 알아서 사용하라는 개념이다.
main.cpp
#include <iostream>
using namespace std;
int add(int a, int b);
int main()
{
cout << add(1, 2) << endl;
return 0;
}
그리고 사실, `int add(int a, int b)` 앞에 생략되어 있는 키워드가 하나 있다. 바로 `extern`이다.
즉, 원래는 ` extern int add(int a, int b)`으로 작성해야 하며, 어딘가에 실제 정의되는 body가 있다고 알려주는 것이다.
add.cpp
int add(int a, int b)
{
return a + b;
}
하지만, 매번 전방선언 하는 것도 일이고 파일을 분리한 이유는 재사용 하기 위함이기 때문에 헤더 파일을 만들게 된다. 그래서 다음과 같이 사용 가능하다.
main.cpp
#include <iostream>
#include "add.h"
using namespace std;
int main()
{
cout << add(1, 2) << endl;
return 0;
}
add.h
#pragma once
int add(int a, int b);
add.cpp
int add(int a, int b)
{
return a + b;
}
헤더 선언시 부등호와 따옴표 차이
<>으로 선언한 것은 표준 라이브러리나 컴파일러가 지정한 시스템 경로에서만 검색한다. GCC의 경우 /usr/include에 존재하고 MSVC의 경우 Visual Studio 설치 경로에 존재한다.
"" 따옴표로 선언한 것은 현재 소스 파일이 존재하는 디렉터리에서 헤더를 탐색하고, 없다면 시스템 경로에서도 검색을 한다. 같은 이름의 헤더가 로컬과 시스템 경로에 둘 다 있으면 `" "`가 우선 순위가 높다.
보통 외부 라이브러리를 불러와서 사용한다고 하면, 전용 include 디렉토리에 들어가 있기 때문에 <>를 통해 불러와서 쓰는게 일반적이다.
#include <mylib/header.h>
헤더 파일을 불러오는 방식
헤더는 독립적으로 .obj, .o 파일로 컴파일 되지 않는다. 헤더를 include 한다는 것은 단순히 호출한 .cpp 파일쪽에 텍스트 그대로 복사 붙여넣기 되는 형태라 생각하면 된다. 그래서 `#include "add.h"`의 의미는 `"add,h"`의 내용을 복사해서 소스 코드 안에 넣어준다. 그렇기에 헤더 자체는 별도의 오브젝트 파일을 만들지 않고 cpp 파일이 컴파일 될 때 함께 처리된다(인라인 함수 처럼).
그래서 사실 다음과 같은 코드 구조도 멀쩡히 동작을 한다.
main.cpp:
#include <iostream>
#include "add.h"
using namespace std;
int main()
{
cout << add(1, 5) << endl;
return 0;
}
// 출력: 12
add.h:
#pragma once
int add(int x, int y);
add.cpp:
int add(int x, int y)
{
return x + y + x + y;
}
C++에서 클래스 기반의 코드를 작성할 때 add.cpp에서 add.h를 include하는 것이 익숙해져서 꼭 그런 형태가 되야한다고 생각했다. 하지만 위의 코드를 이해하자면 헤더 파일의 내용을 그대로 소스로 복붙하게되면서 add함수가 main.cpp에 전방선언이 된 형태가 되고, main.cpp가 컴파일 될 때 add.o는 어딘가에 존재할 것이라는 힌트를 얻게된다. add.cpp가 컴파일 되어 add.o에 실제 add() 함수의 구현부가 존재하게 되고, 링크 단계에서 main.o 안에서 어딘가에 존재하는 add(int, int)를 찾으려 할 때 add.o안에 정의가 존재하니 링크하게 된다.
링킹 에러
보통 컴파일 에러는 함수의 declaration만 알고 있으면 발생하지 않는다. 하지만 declaration만 존재하여 실제 definition을 찾아 링크하려고 할 때 definition을 찾지 못하면 링킹 에러가 발생한다.
헤더 가드
소스 파일을 분리해서 독립성을 갖추게 만들었다면, 그 독립적인 모듈들을 사용하는 곳에서 헤더 파일을 불러오고, 서로 참조하는 과정에서 똑같은 헤더들이 여러 번 include 될 수 있다. 이렇게 되면 각종 에러가 발생할 수 있는데, 아래의 예시는 C2084의 "함수에 이미 본문이 있습니다." 라는 에러가 발생한다.
main.cpp
#include <iostream>
#include "my_work.h"
#include "add.h"
using namespace std;
int main()
{
doSomething();
return 0;
}
add.h
//#pragma once
int add(int a, int b)
{
return a + b;
}
my_work.h
//#pragma once
#include "add.h"
void doSomething()
{
add(1, 2);
}

이 에러가 발생하는건, 헤더 파일을 가져오면 되는 내용이 최종적으로 이렇게 된다.
main.cpp
#include <iostream>
// #include "my_work.h"
// #include "add.h"
using namespace std;
// --- my_work.h를 가져온 결과 ---
int add(int a, int b)
{
return a + b;
}
void doSomething()
{
add(1, 2);
}
// ---------
// --- add.h를 가져온 결과 ---
int add(int a, int b)
{
return a + b;
}
// ---------
int main()
{
doSomething();
return 0;
}
이런걸 방지하려면 헤더 파일 최상단에 `#pragma once`를 붙이면 된다. 헤더 파일 생성하면 visual studio에서는 자동으로 최상단에 헤더 가드를 작성해준다. 근데, 이건 MSV에서 사용가능한 방식이고, 원래는 다음처럼 작성한다.
add.h
//#pragma once
#ifndef ADD_H
# define ADD_H
int add(int a, int b)
{
return a + b;
}
# endif'C,C++ > 홍정모의 따배C++' 카테고리의 다른 글
| [C++] 전역 변수, 내부 연결, 외부 연결, 헤더 include 주의점 (0) | 2025.12.20 |
|---|---|
| [C++] 함수 내의 static 변수 선언 (0) | 2025.11.30 |
| [C++] 변수 초기화 방식 (0) | 2025.09.25 |
| [C++] 전처리기, 매크로 (0) | 2025.09.21 |
| [C++] 네임스페이스 (0) | 2025.09.13 |