Центр космических полетов имени Годдарда ( GSFC ) НАСА утверждает, что заголовки в C должны иметь возможность включать заголовок в исходный файл в качестве единственного заголовка и что этот код использует средства, предоставляемые этим Затем заголовок будет скомпилирован.
Это означает, что заголовок должен быть как автономным, так и идемпотентным:
- автономный - все необходимые типы определяются включением соответствующих заголовков, если это необходимо.
- идемпотент - компиляции не ломаются, даже если они включены несколько раз.
Преимущество этого правила состоит в том, что если кому-то нужно использовать заголовок, ему не нужно пытаться определить, какие другие заголовки также должны быть включены - они знают, что заголовок предоставляет все необходимое.
Возможным недостатком является то, что некоторые заголовки могут быть включены много раз; Вот почему защита заголовков с несколькими включениями имеет решающее значение (и поэтому компиляторы стараются избегать повторного включения заголовков, когда это возможно).
Осуществление
Это правило означает, что если заголовок использует тип - такой как 'FILE *
' или 'size_t
' - то он должен гарантировать, что соответствующий другой заголовок (например, <stdio.h>
или <stddef.h>
) должен быть включен. Следствием, часто забываемым, является то, что заголовок не должен включать в себя любой другой заголовок, который не , необходимый пользователю пакета для использования пакета. Другими словами, заголовок должен быть минимальным.
Кроме того, правила GSFC предоставляют простой метод, гарантирующий, что это то, что происходит:
- В исходном файле, который определяет функциональность, заголовок должен быть первым в списке.
Следовательно, предположим, у нас есть Волшебная сортировка.
magicsort.h
#ifndef MAGICSORT_H_INCLUDED
#define MAGICSORT_H_INCLUDED
#include <stddef.h>
typedef int (*Comparator)(const void *, const void *);
extern void magicsort(void *array, size_t number, size_t size, Comparator cmp);
#endif /* MAGICSORT_H_INCLUDED */
magicsort.c
#include <magicsort.h>
void magicsort(void *array, size_t number, size_t size, Comparator cmp)
{
...body of sort...
}
Обратите внимание, что заголовок должен включать некоторый стандартный заголовок, который определяет size_t
; самый маленький стандартный заголовок, который делает это, <stddef.h>
, хотя некоторые другие также делают это (<stdio.h>
, <stdlib.h>
, <string.h>
, возможно несколько других).
Кроме того, как уже упоминалось ранее, если файлу реализации требуются другие заголовки, пусть будет так, и для некоторых дополнительных заголовков это вполне нормально. Но файл реализации ('magicsort.c') должен включать их сам, а не полагаться на свой заголовок для их включения. Заголовок должен включать только то, что нужно пользователям программного обеспечения; не то, что нужно разработчикам.
Конфигурационные заголовки
Если ваш код использует заголовок конфигурации (например, GNU Autoconf и сгенерированный 'config.h'), вам может понадобиться использовать его в 'magicsort.c':
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "magicsort.h"
...
Это единственный раз, когда я знаю, что частный заголовок модуля - не самый первый заголовок в файле реализации. Однако условное включение config.h, вероятно, должно быть в самом файле magicsort.h.
Обновление 2011-05-01
Ссылка, указанная выше, больше не работает (404). Стандарт C ++ (582-2003-004) можно найти по адресу EverySpec.com ; стандарт C (582-2000-005), по-видимому, отсутствует в действии.
Рекомендации стандарта C были следующими:
§2.1 ЕДИНИЦЫ
(1) Код должен быть структурирован как единицы или как отдельные заголовочные файлы.
(2) Единица должна состоять из одного файла заголовка (.h) и одного или нескольких файлов тела (.c). Все вместе заголовочные файлы и файлы тела называются исходными файлами.
(3) Файл заголовка блока должен содержать всю соответствующую информацию, требуемую клиентским блоком. Единицы
клиент должен получить доступ только к заголовочному файлу, чтобы использовать устройство.
(4) Файл заголовка блока должен содержать операторы #include для всех других заголовков, необходимых для заголовка блока. Это позволяет клиентам использовать единицу, включая один заголовочный файл.
(5)Файл тела модуля должен содержать инструкцию #include для заголовка модуля перед всеми остальными операторами #include. Это позволяет компилятору проверить, что все необходимые операторы #include находятся в
заголовочный файл.
(6) Файл тела должен содержать только функции, связанные с одним модулем. Один файл тела не может
обеспечить реализации для функций, объявленных в разных заголовках.
(7) Все клиентские блоки, которые используют любую часть данного блока U, должны включать файл заголовка для блока U; этот
гарантирует, что есть только одно место, где определены объекты в блоке U. Клиентские подразделения могут
вызывать только функции, определенные в заголовке блока; они могут не вызывать функции, определенные в
тело, но не объявлено в заголовке. Клиентские блоки могут не иметь доступа к переменным, объявленным в теле
но не в шапке.
A компонент содержит одну или несколько единиц. Например, математическая библиотека является компонентом, который содержит
несколько единиц, таких как вектор, матрица и кватернион.
Автономные заголовочные файлы не имеют связанных тел; например, заголовок общих типов делает
не объявлять функции, поэтому он не нуждается в теле.
Некоторые причины наличия нескольких файлов тела для юнита:
- Часть кода тела зависит от аппаратного обеспечения или операционной системы, а остальное является общим.
- Файлы слишком велики.
- Устройство представляет собой общий пакет утилит, и некоторые проекты будут использовать только некоторые из
функции. Помещение каждой функции в отдельный файл позволяет компоновщику исключать те, которые не
используется из окончательного изображения.
§2.1.1 Заголовок включает обоснование
Этот стандарт требует, чтобы заголовок блока содержал #include
операторов для всех остальных требуемых заголовков
по заголовку блока. Размещение #include
для заголовка блока первым в теле блока позволяет компилятору
убедитесь, что заголовок содержит все необходимые операторы #include
.
Альтернативный дизайн, не разрешенный этим стандартом, не допускает операторов #include
в заголовках; все
#include
делаются в файлах тела. В этом случае заголовочные файлы должны содержать #ifdef
операторов, которые проверяют
что требуемые заголовки включены в правильном порядке.
Одним из преимуществ альтернативного дизайна является то, что список #include
в основном файле
список зависимостей необходим в make-файле, и этот список проверяется компилятором. Со стандартом
дизайн, инструмент должен быть использован для создания списка зависимостей. Тем не менее, все отрасли
рекомендуемые среды разработки предоставляют такой инструмент.
Основным недостатком альтернативного дизайна является то, что если меняется требуемый список заголовков устройства, каждый файл
который использует этот блок, должен быть отредактирован для обновления списка операторов #include
. Также необходим заголовок списка
для библиотеки компилятора модуль может отличаться для разных целей.
Другим недостатком альтернативного дизайна является то, что заголовочные файлы библиотеки компилятора и другие сторонние
файлы, должны быть изменены, чтобы добавить обязательные операторы #ifdef
.
Другой распространенной практикой является включение всех файлов заголовков системы перед любыми файлами заголовков проекта, в
файлы тела. Этот стандарт не следует этой практике, потому что некоторые файлы заголовков проекта могут
зависеть от системных заголовочных файлов, потому что они используют определения в системном заголовке, или
потому что они хотят переопределить определение системы. Такие заголовочные файлы проекта должны содержать #include
операторы для системных заголовков; если тело включает их в первую очередь, компилятор не проверяет это.
Стандарт GSFC доступен через интернет-архив 2012-12-10
Информация Предоставлено Эрик С. Буллингтон :
Ссылочный стандарт кодирования NASA C доступен и загружен через интернет-архив:
http://web.archive.org/web/20090412090730/http://software.gsfc.nasa.gov/assetsbytype.cfm?TypeAsset=Standard
Секвенирование
Вопрос также задает:
Если да, нужно ли мне также ставить его (#include
строки) между #ifndef
и #define
или после #define
.
Ответ показывает правильный механизм - вложенные включения и т. Д. Должны быть после #define
(а #define
должна быть второй строкой без комментариев в заголовке) - но это не объясняет, почему это правильный.
Подумайте, что произойдет, если вы поместите #include
между #ifndef
и #define
. Предположим, что сам другой заголовок включает в себя различные заголовки, возможно даже косвенно #include "magicsort.h"
. Если второе включение magicsort.h
происходит до #define MAGICSORT_H_INCLUDED
, тогда заголовок будет включен второй раз, прежде чем будут определены типы, которые он определяет. Таким образом, в C89 и C99 любое имя типа typedef
будет ошибочно переопределено (C2011 позволяет переопределять их в один и тот же тип), и вы получите издержки на обработку файла несколько раз, победив цель заголовка гвардии в первую очередь. По этой же причине #define
является второй строкой и не записывается непосредственно перед #endif
. Приведенная формула надежна:
#ifndef HEADERGUARDMACRO
#define HEADERGUARDMACRO
...original content of header — other #include lines, etc...
#endif /* HEADERGUARDMACRO */