고수들이 절대 가르쳐 주지 않는 C/C++ 프로그래밍 팁 #1 A/S - include guard 관련 이슈

Posted at 2007.06.19 16:30 // in S/W개발/고절가주팁 // by 김윤수


고절가주팁 #1에서 헤더 파일이 중복 포함되는 것을 방지하는 방법을 설명드렸습니다. 이 팁을 공개하고 났더니 여러분들이 댓글을 달아 주시더군요. 그 중에 #pragma once 컴파일러 지시자는 한 번 더 자세히 알아볼 필요가 있을 것 같아서 이글을 쓰게 됐습니다.

일단 Microsoft Visual C++ 와  GCC는 확실히 #pragma once 지시자를 지원하는 것으로 보입니다. 다른 컴파일러에서 제가 #pragma once 를 사용해 본 적이 없어서 지원 여부를 잘 모르겠네요. 혹시 다른 컴파일러 사용하시는 분이 있으시다면 #pragma once 지원 여부를 알려주시면 정말 감사하겠습니다.

알단 제가 제시했던 헤더 파일 중복 포함 방지 기법은 소위, include guards라고 불리더군요(누가 이렇게 이름을 잘 짓나 몰라요 참~). 이 include guards 와 #pragma once 의 가장 큰 차이점은 include guards 는 한 번 읽은 헤더 파일도 일단 헤더 파일의 내용을 다 읽어야 하는 반면, #pragma once 의 경우에는 각 파일별로 프리프로세서가 include 한 상태를 기억하면 되므로 한 번 읽은 헤더 파일은 읽지 않아도 됩니다. 따라서 컴파일 속도 측면에서 보면 #pragma once 가 더 유리합니다.

그리고, include guards 의 경우, 각 헤더 파일에 대응하는 매크로 이름이 서로 충돌할 가능성이 있다는 것입니다. 여러 라이브러리를 섞어 쓰다 보면 우연히 헤더 파일 대응 매크로 이름이 충돌하여 헤더 파일이 포함되어야 함에도 불구하고, 포함되지 않는 경우가 발생할 수 있다는 것입니다. 이런 문제는 대규모 프로젝트라면 항상 발생할 수 있는 문제이지요. 이런 문제가 발생할 가능성을 줄이려면 include guards 명명 규칙을 나름대로 정해서 매크로 이름이 충돌되지 않도록 해야 합니다. 예를 든다면 매크로명을 _MODULE_HEADER_H 와 같은 방식으로 정의할 수 있을 것입니다. 모듈명이 먼저 나온 후에 헤더 파일의 이름을 쓰는 거죠. 예를 들어 dispatcher 모듈의 모듈명 DISP 라고 가정하고, dispatcher.h 파일에 대한 include guards 를 정의한다면 _DISP_DISPATCHER_H 와 같이 명명할 수 있을 것입니다.

#pragma once 도 문제가 없는 건 아닙니다. 프리프로세서에서 #pragma once 를 처리하는 데 버그가 있거나 파일 시스템 상에서 파일의 동일성을 파악하는 데 어려움이 있다거나 하면 문제가 좀 심각해 집니다. 예를 들어, Unix 계열의 OS 에서는 서로 다른 경로명을 가지고 있지만 결국 같은 파일을 가리키는 경우가 있습니다(hard link, soft link). 이렇게 되면 속수 무책으로 헤더 파일을 중복 포함되겠지요. #pragma once 를 썼는데도 중복 포함으로 인한 에러 메시지를 보게 된다면 문제를 찾기가 쉽지 않을 것입니다.

게다가 고절가주팁 #1의 댓글에서 몇 몇 분이 이미 밝혀 주셨지만 #pragma once 는 결정적으로 표준이 아니라는 문제가 있습니다. 컴파일러마다 지원 여부가 다 달라진다는 것이죠. 따라서 #pragma once 를 쓰게 되면 이식성이 좀 떨어지게 됩니다. 이런 이유 때문인지 GCC 에서는 #pragma once 를 obsolete feature 로 규정하고, include guards 형태를 #pragma once 와 거의 동일하게 처리하는 기능을 추가했다고 하더군요(include guard optimization).

마지막으로 한 가지 더 말씀드리고 싶은 것은 include guard optimization 입니다.지금까지 설명드린 include guard 는 헤더 파일 안에 관련 매크로가 정의되기 때문에 internal include guard 라고 합니다. 그럼 internal 이 있다면 external 이 있어야 겠지요 ? 다음과 같이 하는 걸 external include guard 라고 합니다.

#ifndef HDR_H
#include "hdr1.h"
#endif

말 그대로 헤더 파일 외부에서 guard 매크로를 테스트하는 것이죠. 이렇게 하면 internal include guard 의 문제점인 컴파일 속도 문제도 해결할 수 있을 것입니다. 이런식으로 internal include guard 와 external include guard 를 같이 사용하면 중복 포함 방지와 컴파일 속도 개선 두 마리 토끼를 다 잡을 수 있게 됩니다.

이식성이 개발 과제의 중요한 목표일 경우에는 이식성이 떨어지는 #pragma once 를 사용하기 보다는 internal include guard 와 external include guard 를 활용해서 헤더 파일이 중복 포함되는 걸 방지하는 것이 더 바람직한 방법으로 생각되네요.

참고문헌
Wikipedia #pragma once
Wikipedia include guards
include guard 와 #pragma once 의 차이점
include guard optimization

이 글은 스프링노트에서 작성되었습니다.

제 글이 유익하셨다면 오른쪽 버튼을 눌러 제 블로그를 구독하세요. ->
블로그를 구독하는 방법을 잘 모르시는 분은 2. RSS 활용을 클릭하세요.
RSS에 대해 잘 모르시는 분은 1. RSS란 무엇인가를 클릭하세요.

신고
  1. 하트빛

    2007.06.19 17:54 신고 [수정/삭제] [답글]

    최근 Freescale Semiconductor사에 인수된 Metrowerks CodeWarrior도 #pragma once 지원합니다. CodeWarrior는 ARM9 등 임베디드 개발 환경으로 꽤 사용자층이 두터운 개발 환경입니다.

    저는 오히려, #pragma once를 지원하지 않는 (그러면서도 널리 쓰이는) 컴파일러가 있는 지를 더 알고 싶습니다.

  2. 김윤수

    2007.06.19 21:35 신고 [수정/삭제] [답글]

    그렇군요. 정보 감사합니다. #pragma once 를 지원하지 않으면서도 널리 쓰이는 컴파일러가 있는지를 알고 싶다는 말씀은 (그런 컴파일러가 별로 없을 것이므로) #pragma once 를 쓰는 게 include guard 를 쓰는 것보다 더 낫다고 말씀하고 싶으셔서 그런 건가요 ?

    그렇다 하더라도 만약 portability 가 개발 과제의 중요한 목표일 경우에는 컴파일러의 기능에 의존적인 코드를 짜는 것은 별로 좋지는 않을 것 같습니다. 물론 portability 가 중요한 목표가 아니라면 꼭 그럴 필요는 없겠지요.

    그래서 본문을 약간 수정했습니다.

  3. Jaws

    2007.06.19 21:39 신고 [수정/삭제] [답글]

    #pragma once가 Visual C++계열과 GCC까지 지원을 하는 시점에서
    이미 매우 많은 경우에 문제가 되진 않을 것 같습니다.

    심지어 위에분 말씀대로 임베디드쪽 사용자층이 두터운 컴파일러도 그러하다면 더욱이..
    하지만 이쪽 분야가 워낙 넓어서 특수한 경우가 없다고 볼순 없을 듯합니다.
    PC프로그램 작성할 때 C/C++컴파일러를 직접 만들어서 작성하는 경우까지 있으니까요.

    재밌는건 표준이 아니라 이식이 안된다 하더라도
    약간의 손가락노동이면 되는게 아닌가 하는.. ㅎ
    거기다 #pragma once 의 문제점도 지적을 해주셧으니,
    사용여부 판단이야 다들 알아서 하실거라 생각되네요.

    좋은 정보 얻어갑니다.

    • 김윤수

      2007.06.19 22:06 신고 [수정/삭제]

      음... portability 가 *정말 정말* 중요한 요구사항이라면 #pragma once 는 쓰지 말아야 할 것 같습니다. #pragma once 를 지원하지 않는 컴파일러로 해당 S/W를 옮기려면 손노동을 해야 하는데... 그 S/W 가 방대하다면 일일이 손노동을 해야 하는게 만만치 않게 될 것이니까요. portability가 정말 중요한 대규모의 프로젝트를 생각한다면 쓰지 않는 게 나을 것 같습니다.

      그 외의 경우에는 말씀하신 대로 잘 판단해서 쓰시면 될 듯 하네요

  4. 최익필

    2008.06.04 12:47 신고 [수정/삭제] [답글]

    컴파일러 개발사들이 표준시켰으면 좋겠다는 생각이 듭니다.
    .. #pragma once 는 정말 편하거든요.

  5. 호빗코더

    2015.04.23 01:30 신고 [수정/삭제] [답글]

    이렇게 머리에 쏙쏙 들어올수가!!
    정말 너무 감명깊게 읽었습니다. 포스팅 제목(고수들이 절대 가르쳐 주지않는)에 한번 놀라고 포스팅 내용이 너무 좋아서 또한번 놀라게 됩니다 ^^ 잘읽고 갑니다.

댓글을 남겨주세요.

티스토리 툴바