본문 바로가기

코딩/C++,C

[C++]헤더파일과 cpp파일의 동작방법

m.blog.naver.com/PostView.nhn?blogId=et3569&logNo=130099887905&proxyReferer=https:%2F%2Fwww.google.com%2F

 

헤더파일과 cpp 파일과의 관계

[정리] 1. 컴파일할 때, #include를 해당하는 헤더파일의 내용으로 치환한다. => 링크 때는 없어도 됨 2...

blog.naver.com

[정리]

1. 컴파일할 때, #include를 해당하는 헤더파일의 내용으로 치환한다. => 링크 때는 없어도 됨

2. 함수의 prototype 선언만 있는 것을 call해도, 컴파일까진 무조건 된다.

3. 변수의 extern 선언만 있는 것을 참조해도, 컴파일까진 무조건 된다.

4. 하나의 프로젝트 안에서의 변수/함수 이름은 고유해야한다. (const 변수 예외)

5. 1과 4에서 도출된 결과로, 헤더 파일내에서 변수나 함수정의는 피해야한다. (const 변수 빼공)

자세한 것은 아래의 내용을 읽어봅시다!!!

 

 

 

위의 프로젝트를 바탕으로 연구를 해보았다.

 

(1)

헤더파일은 컴파일 시에 큰 역할을 한다.

일단 여기서는 door.cpp와 main.cpp는 door.h를 include하고 있다고 가정한다.

door.h에다가

     void fake(void);

라고 prototype만 선언해놓고 실제로 정의는 아무데서도 하지 않았고

door.cpp에서 fake();라는 구문을 적었다.

실제로 컴파일까진 된다. 하지만 링크는 안된다.

이러한 근거를 바탕으로 헤더파일은 compile시에 매우 중요하다.

 

 

(2)

컴파일 된 상태에서는 cpp들끼리 의사소통이 가능하다.

흠.. 말이 좀 이상한데... 이 것도 실제로 해보았다.

door.h ==> void someFunc();

라고 선언해놓고 main.cpp내에서 #include "door.h"를 선언했다고  하자.

main.cpp의 main함수에서 someFunc()을 콜해도 컴파일까진 된다.

물론 링크가 되기 전까지는 main.cpp는 someFunc()이 어느 곳에 존재하고 있는지 알 수가 없다.

사실 난 이 함수를 door.cpp에서 정의하였다. (door.cpp에서는 door.h를 include하지 않은 상태)

간단히 정리해보자.

 

door.h => someFunc()의 prototype만 선언.

main.cpp => door.h를 인클루드 / someFunc()을 호출

door.cpp => someFunc()을 정의

 

과연 링크까지 잘 될까? 난 매우 호기심에 가득차 링크를 해보았다.

링크결과는 대성공이었다.

 

위의 두 가지 실험에서 내가 내린 결론은 무엇이라고 생각하는가?

헤더 파일은 왜 존재하는지 나 나름대로의 생각을 써보자면

바로 cpp들간의 서로의 의사소통을 위해서이다.

뭐 좀 더 고급스러운 말로 interface를 통일시키기 위해서라고나 할까...

컴파일되면 헤더 파일은 영향력을 얼마나 가지고 있을까?

그 것을 시험해보기 위해서 난

 

(3)

컴파일을 한 후에 헤더파일을 삭제해버리고

링크를 해보는 작업을 해보았다.

결과는................ 링크 대성공이었다.

 

그럼, 헤더파일에서 변수를 선언하면 어떻게 되는건가?

(또는 함수를 prototype만 선언하는 것이 아니라 아예 정의해버리던가)

변수는 interface랑 아무 상관도 없어보인다.

아마도 변수나 함수의 정의는 컴파일 당시에 cpp에 포함되어버리는 것 같다. (가정)

 

(4)

아 헷갈리니까 좀전에 내린 가정을 토대로 실험을 좀 더 해보자.

일단 갑자기 궁금해져서 door.cpp와 main.cpp안에 

함수 오버로딩이 안되도록, (파라미터/리턴형/함수이름)이 동일한 함수를 선언해봤다.

예상대로 각각의 컴파일은 성공하지만, 링크는 실패 (redefinition!!!)

==>> 하나의 프로젝트내의 cpp에서 함수나 변수의 이름은 하나여야한다. (const 변수는 예외. 아래에 나와있음)

 

(5)

그렇다면 이번엔 헤더에 함수 하나를 아예 정의해서

door.cpp와 main.cpp 두 개에서 동시에 #include를 해보았는데

내 예상과 다르게 링크단계에서 실패를 했다. (redefinition!!!)

변수도 마찬가지였다.

 

 

 

실험의 최종 결론>

헤더 파일은 compile하는 과정 중에

callee의 실제 제공되는 interface와 caller가 체감하는 interface의

synchronization을 위해서 존재한다.

컴파일이 된 이후에 헤더 파일은 쓸모 없는 존재이다.

헤더 파일에서 변수 선언이나 함수 정의(prototype이 아닌..)는 피해야한다.

왜냐하면 두 개 이상의 cpp에서 include할 시에 redefinition 에러가 나기 때문이다.

만약 에러가 안난다고 가정해보자.

헤더를 포함하지 않았던 제 3의 cpp에서 그러한 변수나 함수를 참조하게 되면

어떤 cpp것을 참조하는지 알 수 없기 때문에 말이 안되는 상황이 발생한다.

 

 

 

※ 지금까지 나의 착각 ※

내가 착각하고서 프로그래밍했던 사실이 있다.

함수의 prototype만 선언되어도, 컴파일은 잘 된다는 사실이다.

 

void hi(void);

 

int main(){
 hi();
 return 0;
}

 

위의 코드는 컴파일까지는 무조건 되는 코드다.

또한 함수 prototype 선언에 대응되는 것이 변수의 extern 선언이다.

 

extern i;

 

int main(){
 i++;
 return 0;
}

 

위의 코드도 컴파일까지는 무조건 되는 코드다.

 

 

 

※ 지금까지 생각해왔던 헤더 파일 ※

전처리기는 #include 되있는 곳을 헤더 파일의 내용으로 치환시킨다고 알고 있었는데

그렇게 알고 있던 것이 참이었을까 거짓이었을까?

그 것은 ""이었다. 이해가 안간다면 위의 내용을 한 번 더 보자.

 

 

 

※ 새롭게 알게 된 사실 ※

C++ 책에 나와있는 구문에서 이해가 안가는게 있었다.

헤더에 const로 변수를 선언하면

두 개 이상의 cpp에서 include를 하여도 링크시에 아무 에러가 안나는 것이었다.

(실제로 테스트해보니 그랬다 -ㅅ- const 넣느냐 안넣느냐의 엄청난 차이)

그 이유를 생각해보니까...

 

const의 경우 수정이 불가능하다. 참조만 가능하다.

수정이 불가능하기 때문에

여러 개의 cpp에서 선언되더라도 어차피 값은 계속 유지되고

고치기 못하기 때문에 "어딜 고쳐야하지?"라는 고민 따위는 하게 될 이유가 없다.

 

이 것이 내가 생각한 const로는 변수가 겹쳐도 링크가 되는 이유이다.

 

실제로 헤더 파일 차원을 떠나서

여러 개의 cpp에서 같은 이름, 같은 타입으로 const 변수를 선언해보았다.

abc.cpp에서는 const int j = 0;

def.cpp에서는 const int j = 1;

 

링크까지 성공했고

def.cpp에서 j를 참조하게 되면 1을 읽게 되는 현상까지 발견했다.