수색…


소개

현대 C에서 헤더 파일은 올바르게 설계되고 사용해야하는 중요한 도구입니다. 그것들은 컴파일러가 프로그램에서 독립적으로 컴파일 된 부분을 교차 검사 할 수있게합니다.

헤더는 시설 집합의 소비자가 필요로하는 유형, 함수, 매크로 등을 선언합니다. 이러한 기능을 사용하는 모든 코드에는 헤더가 포함됩니다. 이러한 기능을 정의하는 모든 코드에는 헤더가 포함됩니다. 이를 통해 컴파일러는 사용과 정의가 일치하는지 확인할 수 있습니다.

소개

C 프로젝트에서 헤더 파일을 만들고 사용할 때 따라야 할 지침이 많이 있습니다.

  • 이분법

    헤더 파일이 번역 단위 (TU)에 여러 번 포함되어 있으면 빌드를 중단해서는 안됩니다.

  • 자체 봉쇄

    헤더 파일에 선언 된 기능이 필요한 경우 다른 헤더를 명시 적으로 포함하지 않아도됩니다.

  • 최소

    빌드가 실패하지 않으면 헤더에서 정보를 제거 할 수 없어야합니다.

  • 사용 내용 포함 (IWYU)

    C보다 C ++에 더 많은 관심이 있지만 그럼에도 불구하고 C에서도 중요합니다. TU의 코드 ( code.c 라고 code.c )가 헤더 ( "headerA.h" )에서 선언 한 기능을 직접 사용하면 code.c 는 TU에 직접 포함되어 있어도 #include "headerA.h" 해야합니다 또 다른 헤더 (호출 "headerB.h" 순간, 일)를 포함하는 "headerA.h" .

때로는 이러한 지침 중 하나 이상을 위반할만한 충분한 이유가있을 수 있지만 규칙을 어 기고 위반했을 때의 결과를 알고 있어야합니다.

멱등 원

특정 헤더 파일이 변환 단위 (TU)에 두 번 이상 포함되어 있으면 컴파일 문제가 없어야합니다. 이것은 '멱수 (idempotence)'라고 불린다. 당신의 우두머리는 멱등수가되어야합니다. #include <stdio.h> 가 한 번만 포함되었는지 확인해야한다면 얼마나 어려울 지 생각해보십시오.

멱등 원을 달성하는 방법에는 헤더 가드와 #pragma once 지시문이 있습니다.

헤더 경비원

헤더 가드는 간단하고 신뢰할 수 있으며 C 표준을 준수합니다. 헤더 파일의 첫 번째 주석이 아닌 행은 다음과 같은 형식이어야합니다.

#ifndef UNIQUE_ID_FOR_HEADER
#define UNIQUE_ID_FOR_HEADER

마지막 비 주석 행은 #endif 이어야하며 선택적으로 뒤에 주석이 있어야합니다.

#endif /* UNIQUE_ID_FOR_HEADER */

다른 #include 지시문을 포함한 모든 운영 코드는이 줄 사이에 있어야합니다.

각 이름은 고유해야합니다. 종종 HEADER_H_INCLUDED 와 같은 이름 체계가 사용됩니다. 오래된 코드 중 일부는 헤더 가드 (예 : <stdio.h> #ifndef BUFSIZ 로 정의 된 심볼을 사용하지만 고유 한 이름만큼 신뢰할 수 없습니다.

한 가지 옵션은 생성 된 MD5 (또는 다른) 해시를 헤더 가드 이름으로 사용하는 것입니다. 구현에 예약 된 이름을 자주 사용하는 시스템 헤더가 사용하는 체계를 에뮬레이트하는 것을 피해야합니다. 밑줄로 시작하는 이름 뒤에는 밑줄이나 대문자가옵니다.

#pragma once 지시문

또는 일부 컴파일러는 #pragma once 지시어를 지원합니다.이 지시문은 헤더 가드에 대해 표시된 세 줄과 동일한 효과를냅니다.

#pragma once

#pragma once 를 지원하는 컴파일러에는 MS Visual Studio와 GCC 및 Clang이 포함됩니다. 그러나 이식성이 문제가되면 헤더 가드를 사용하거나 두 가지를 모두 사용하는 것이 좋습니다. 최신 컴파일러 (C89 이상을 지원하는 컴파일러)는 인식하지 못하는 pragma ( '구현시 인식하지 못하는 pragma는 무시됩니다')를 무시하고 GCC의 구버전을 그렇게 무시하지 않아야합니다.

자체 봉쇄

최신 헤더는 자체 포함되어야합니다. 즉, header.h 정의 된 기능을 사용해야하는 프로그램은 해당 헤더 ( #include "header.h" )를 포함 할 수 있으며 다른 헤더를 먼저 포함해야하는지 여부는 걱정하지 않아도됩니다.

권장 사항 : 헤더 파일은 자체 포함되어야합니다.


역사적 규칙

역사적으로 이것은 경미한 논쟁의 대상이었습니다.

AT & T Indian Hill C 스타일 및 코딩 표준에 따르면,

헤더 파일은 중첩되어서는 안됩니다. 따라서 헤더 파일의 프롤로그는 헤더가 작동하기 위해 #include 포함해야하는 다른 헤더를 설명해야합니다. 많은 수의 헤더 파일을 여러 소스 파일에 포함시키는 극단적 인 경우에는 모든 공통 #include 를 하나의 포함 파일에 넣는 것이 허용됩니다.

이것이 자기 봉쇄의 대조입니다.

현대 규칙

그러나 그 이후로 의견은 반대 방향으로 나아 갔다. 소스 파일이 헤더 header.h 의해 선언 된 기능을 사용해야하는 경우, 프로그래머는 다음을 작성할 수 있어야합니다.

#include "header.h"

(명령 줄에 올바른 검색 경로가 설정되어있는 경우에만 해당) 소스 파일에 헤더를 추가하지 않고도 header.h 필요한 사전 필수 헤더가 포함됩니다.

이것은 소스 코드의 모듈성을 향상시킵니다. 또한 코드가 수정되고 10 년 또는 2 년 동안 해킹 된 후에 발생하는 "이 헤더가 추가 된 이유"추측에서 소스를 보호합니다.

NASA 고다드 우주 비행 센터 (GSFC)는 C대한 표준을 코딩 하는 것이보다 현대적인 표준 중 하나입니다. 그러나 지금은 추적하기가 다소 어렵습니다. 헤더에는 자체 포함되어야한다고 명시되어 있습니다. 또한 헤더가 자체 포함되도록하는 간단한 방법을 제공합니다. 헤더의 구현 파일에는 헤더를 첫 번째 헤더로 포함해야합니다. 자체 포함되지 않은 경우 해당 코드는 컴파일되지 않습니다.

GSFC의 이론적 근거는 다음과 같습니다.

§2.1.1 헤더는 이론적 근거를 포함합니다

이 표준은 단위 머리글에 필요한 다른 모든 머리글에 대해 #include 문을 포함하도록 단위 머리글을 요구합니다. 유닛 헤더에 #include 를 먼저 배치하면 컴파일러가 헤더에 필요한 모든 #include 문이 있는지 확인할 수 있습니다.

이 표준에서 허용되지 않는 대체 설계는 헤더에 #include 문을 허용하지 않습니다. 모든 #includes 는 본문 파일에서 수행됩니다. 유닛 헤더 파일은 필요한 헤더가 적절한 순서로 포함되어 있는지 확인하는 #ifdef 문을 포함해야합니다.

대체 디자인의 한 가지 장점은 본문 파일의 #include 목록이 makefile에 필요한 종속성 목록이며 컴파일러에서이 목록을 확인한다는 것입니다. 표준 설계에서는 종속성 목록을 생성하는 데 도구를 사용해야합니다. 그러나 지점 권장 개발 환경은 모두 이러한 도구를 제공합니다.

대체 설계의 주요 단점은 유닛의 필수 헤더 목록이 변경되면 #include 문 목록을 업데이트하기 위해 해당 유닛을 사용하는 각 파일을 편집해야한다는 것입니다. 또한 컴파일러 라이브러리 단위에 필요한 헤더 목록은 다른 대상에서 다를 수 있습니다.

대체 디자인의 또 다른 단점은 컴파일러 라이브러리 헤더 파일 및 기타 타사 파일을 수정하여 필수 #ifdef 문을 추가해야한다는 것입니다.

따라서 자체 봉쇄는 다음을 의미합니다.

  • 헤더 header.h 에 새로운 중첩 헤더 extra.h 가 필요한 경우 header.h 를 사용하는 모든 소스 파일을 검사하여 extra.h 를 추가 extra.h 여부를 확인할 필요가 extra.h .
  • 헤더 header.h 가 더 이상 특정 헤더 notneeded.h 를 포함 할 필요가 없으면 header.h 를 사용하는 모든 소스 파일을 검사하여 notneeded.h 안전하게 제거 할 수 있는지 확인할 필요가 notneeded.h (단, 사용하는 항목 포함 참조).
  • 선행 필수 헤더를 포함하는 올바른 순서를 설정할 필요가 없습니다 (작업을 올바르게 수행하기 위해 토폴로지 정렬이 필요함).

자체 봉쇄 확인

멱등 원과 헤더 파일의 자체 포함을 테스트하는 데 사용할 수있는 스크립트 chkhdr 에 대한 정적 라이브러리와의 링크를 참조하십시오.

최소

헤더는 중요한 일관성 검사 메커니즘이지만 가능한 작아야합니다. 특히 구현 파일에 다른 헤더가 필요하기 때문에 헤더가 다른 헤더를 포함하면 안된다는 뜻입니다. 헤더는 서술 된 서비스의 소비자에게 필요한 헤더만을 포함해야한다.

예를 들어, 함수 헤더 중 하나가 FILE * 유형 (또는 <stdio.h> 에서만 정의 된 다른 유형 중 하나)을 사용하지 않으면 프로젝트 헤더에 <stdio.h> 포함되지 않아야합니다. 인터페이스가 size_t 사용한다면, 가장 작은 헤더는 <stddef.h> 입니다. 분명히, size_t 를 정의하는 다른 헤더가 포함되면 <stddef.h> 도 포함 할 필요가 없습니다.

헤더가 최소한이라면 컴파일 시간도 최소로 유지됩니다.

다른 많은 헤더를 포함하는 것이 유일한 목적 인 헤더를 고안하는 것이 가능합니다. 거의 모든 소스 파일이 모든 헤더에 설명 된 모든 기능을 실제로 필요로하기 때문에 드물게 좋은 아이디어가 될 수 있습니다. 예를 들어, 일부 표준 헤더가 항상 존재하지는 않으므로 모든 표준 C 헤더를 포함하는 <standard-ch> 고안 할 수 있습니다. 그러나, <locale.h> 또는 <tgmath.h> 의 기능을 실제로 사용하는 프로그램은 거의 없습니다.

사용 내용 포함 (IWYU)

Google의 프로젝트 사용 또는 IWYU는 소스 파일에 코드에 사용 된 모든 헤더가 포함되도록합니다.

소스 파일 가정 source.c 헤더가 포함 arbitrary.h 차례로 우연히 포함 freeloader.h 하지만, 소스 파일은 명시 적으로 독립적는에서 시설을 사용 freeloader.h . 모든 것이 시작하는 것이 좋습니다. 그런 다음 언젠가 arbitrary.h 가 변경되어 클라이언트가 더 이상 freeloader.h 의 기능을 필요로하지 않게됩니다. 갑자기, source.c 는 IWYU 기준에 맞지 않아 컴파일을 중단합니다. 의 코드 때문에 source.c 명시 적으로의 시설 사용 freeloader.h 명시 적이 있었다한다 -, 그것을 사용하는 것을 포함해야 #include "freeloader.h" 너무 소스입니다. ( Idempotency 는 문제가 없다는 것을 확신했을 것입니다.)

IWYU 철학은 인터페이스를 합리적으로 변경하더라도 코드가 계속 컴파일 될 확률을 최대화합니다. 분명히 코드가 게시 된 인터페이스에서 이후에 제거되는 함수를 호출하는 경우 준비가 필요하지 않아 변경이 필요하지 않게 될 수 있습니다. 이것이 가능한 경우 API에 대한 변경을 피하고 여러 릴리스에 대해 지원 중단주기가있는 이유입니다.

이것은 표준 헤더가 서로 포함 할 수 있기 때문에 C ++에서 특별한 문제입니다. 소스 파일 file.cpp 는 하나의 플랫폼에 다른 헤더 header2.h 가 포함 된 하나의 헤더 header1.h 를 포함 할 수 있습니다. file.cpp 의 시설을 사용하는 것이 판명 수 header2.h 뿐만 아니라. 처음에는 문제가되지 않을 것입니다. header1.h 포함되어 header2.h 코드가 컴파일됩니다. 다른 플랫폼 또는 현재 플랫폼의 업그레이드에서 header1.h 가 더 이상 header2.h 포함하지 않도록 수정 될 수 있으며, 그 결과 file.cpp 은 결과적으로 컴파일을 중지합니다.

IWYU 문제를 파악하고하는 것이 좋습니다 것입니다 header2.h 직접에 포함 file.cpp . 이렇게하면 컴파일이 계속됩니다. 비슷한 고려 사항이 C 코드에도 적용됩니다.

표기법 및 기타

C 표준은 #include <header.h>#include "header.h" 표기법 사이에 거의 차이가 없다고 말합니다.

[ #include <header.h> ]는 <> 구분 기호 사이의 지정된 시퀀스로 고유하게 식별 된 헤더에 대해 구현 정의 된 위치의 시퀀스를 검색하고 해당 지시문을 헤더의 전체 내용으로 대체합니다. 장소가 지정되는 방법 또는 식별 된 헤더가 구현 정의되는 방법.

[ #include "header.h" ]는 "…" 구분 기호 사이의 지정된 순서로 식별되는 소스 파일의 전체 내용으로 해당 지시문을 #include "header.h" 합니다. 명명 된 소스 파일은 구현 정의 방식으로 검색됩니다. 이 검색이 지원되지 않거나 검색에 실패하면 지시문은 [ #include <header.h> ]와 같이 다시 처리됩니다.

따라서 큰 따옴표로 묶인 형식은 꺽쇠 괄호로 묶은 형식보다 더 많은 곳에서 볼 수 있습니다. 표준에서는 큰 따옴표를 대신 사용하는 경우 컴파일이 작동하더라도 표준 머리글을 꺽쇠 괄호에 포함해야한다고 지정합니다. 마찬가지로, POSIX와 같은 표준은 앵글 브라켓 형식을 사용합니다. 프로젝트에서 정의한 머리글에 큰 따옴표 머리글을 예약하십시오. 외부에서 정의 된 헤더 (프로젝트가 의존하는 다른 프로젝트의 헤더 포함)의 경우 꺾쇠 괄호 표기법이 가장 적합합니다.

컴파일러가 아무런 공간도 허용하지 않더라도 #include 와 헤더 사이에는 공백이 있어야합니다. 공간이 쌉니다.

많은 프로젝트에서 다음과 같은 표기법을 사용합니다.

#include <openssl/ssl.h>
#include <sys/stat.h>
#include <linux/kernel.h>

프로젝트에서 네임 스페이스 컨트롤을 사용할지 여부를 고려해야합니다 (좋은 생각입니다). 기존 프로젝트에서 사용되는 이름을 명확하게 처리해야합니다 (특히 syslinux 모두 잘못된 선택입니다).

이것을 사용한다면, 코드는 조심해야하며 표기법을 일관되게 사용해야합니다.

#include "../include/header.h" 표기법을 사용하지 마십시오.

헤더 파일은 거의 변수를 정의하지 않아야합니다. 전역 변수를 최소한으로 유지하지만 전역 변수가 필요하면 헤더에 선언하고 하나의 적절한 소스 파일에 정의하고 소스 파일에는 선언 및 정의를 교차 점검하는 헤더가 포함됩니다 변수를 사용하는 모든 소스 파일은 헤더를 사용하여 변수를 선언합니다.

결론 : 소스 파일에 전역 변수를 선언하지 않습니다. 소스 파일에는 정의 만 포함됩니다.

헤더 파일은 함수가 둘 이상의 소스 파일에서 필요하면 헤더에 정의되는 static inline 함수를 제외하고는 static 함수를 거의 선언해서는 안됩니다.

  • 소스 파일은 전역 변수와 전역 함수를 정의합니다.
  • 소스 파일은 전역 변수 또는 함수의 존재를 선언하지 않습니다. 변수 또는 함수를 선언하는 헤더가 포함됩니다.
  • 헤더 파일은 전역 변수 및 함수 (및 유형 및 기타 지원 자료)를 선언합니다.
  • 헤더 파일은 ( static ) inline 함수를 제외한 변수 또는 함수를 정의하지 않습니다.

상호 참조



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow