Куда поставить включенные утверждения, заголовок или источник?

Должен ли я помещать его в заголовочный файл или исходный файл? Если заголовочный файл содержит инструкции include, то если я включу этот заголовочный файл в свой исходный код, то будет ли в моем исходном файле все входящие в него файлы, которые были в моем заголовке? Или я просто должен включать их только в исходный файл?

Только put включает в заголовок, если сам заголовок нуждается в них.

Примеры:

  • Функция возвращает тип size_t . Затем #include в файле заголовка .
  • В вашей функции используется strlen . Затем #include в исходном файле.

За эти годы было много разногласий. В свое время было традиционным, что заголовок объявляет только то, что было в любом модуле, к которому он был связан, поэтому у многих заголовков были определенные требования, которые вы #include в определенный набор заголовков (в определенном порядке). Некоторые чрезвычайно традиционные программисты C все еще следуют этой модели (религиозно, по крайней мере в некоторых случаях).

Совсем недавно появилось движение к тому, чтобы сделать большинство заголовков автономными. Если этот заголовок требует чего-то другого, сам заголовок обрабатывает это, гарантируя, что все, что ему нужно, включено (в правильном порядке, если есть проблемы с упорядочением). Лично я предпочитаю это – особенно когда порядок заголовков может быть важен, он решает проблему один раз, вместо того, чтобы требовать от всех, кто его использует, снова решить проблему.

Обратите внимание: большинство заголовков должны содержать только объявления. Это означает, что добавление ненужного заголовка не должно (обычно) оказывать какое-либо влияние на ваш окончательный исполняемый файл. Хуже всего то, что он немного замедляет компиляцию.

Ваш #include s должен иметь файлы заголовков, и каждый файл (источник или заголовок) должен #include включать файлы заголовков, в которых он нуждается. Заголовочные файлы должны #include минимальные файлы заголовков, необходимые, а также исходные файлы, хотя это не так важно для исходных файлов.

Исходный файл будет иметь заголовки, которые он #include s, и заголовки, которые они #include , и так далее до максимальной глубины вложенности. Вот почему вы не хотите лишних #include s в заголовочных файлах: они могут привести к тому, что исходный файл будет содержать много файлов заголовков, которые ему могут не понадобиться, замедляя компиляцию.

Это означает, что вполне возможно, что заголовочные файлы могут быть включены дважды, и это может быть проблемой. Традиционный метод заключается в том, чтобы включить «включить охранников» в файлы заголовков, например, для файла foo.h:

 #ifndef INCLUDE_FOO_H #define INCLUDE_FOO_H /* everything in header goes here */ #endif 

Если заголовочный файл A #includes заголовочные файлы B и C, то каждый исходный файл, #includes A, также будет включать B и C #included . Препроцессор буквально просто выполняет замену текста: везде, где он находит текст, который говорит #include он заменяет его текстом файла foo.h

Существуют разные мнения о том, следует ли #includes в заголовках или исходных файлах. Лично я предпочитаю поместить все #includes в исходный файл по умолчанию, но любые заголовочные файлы, которые не могут компилироваться без других предварительных заголовков, должны #include эти заголовки сами.

И каждый заголовочный файл должен содержать включенную защиту, чтобы предотвратить ее включение несколько раз.

В некоторых средах компиляция будет самой быстрой, если в нее будут включены только файлы заголовков. В других средах компиляция будет оптимизирована, если все исходные файлы могут использовать одну и ту же основную коллекцию заголовков (некоторые файлы могут иметь дополнительные заголовки за пределами общего подмножества). В идеале, заголовки должны быть построены так, что несколько операций #include не будут иметь никакого эффекта. Может быть полезно окружать #include заявлениями с проверками включенного в файл include-guard, хотя это создает зависимость от формата этого защитника. Кроме того, в зависимости от поведения кэширования файлов в системе ненужный #include, чья цель заканчивается полностью # ifdef’ed, может не занять много времени.

Другое дело, что если функция принимает указатель на структуру, можно написать прототип как

 void foo (struct BAR_s * bar);

без определения для BAR_s, которые должны быть в области видимости. Очень удобный подход для предотвращения ненужных включений.

PS – во многих моих проектах будет файл, который ожидается, что каждый модуль будет #include, содержащий такие вещи, как typedefs для целочисленных размеров и несколько общих структур и объединений (например,

 typedef union {
   unsigned long l;
   unsigned short lw [2];
   unsigned char lb [4];
 } U_QUAD;

(Да, я знаю, что у меня были бы проблемы, если бы я перешел к архитектуре большого конца, но поскольку мой компилятор не разрешает анонимные структуры в объединениях, использование именованных идентификаторов для байтов внутри объединения потребует, чтобы к ним обращались как theUnion.b.b1 и т. д., что кажется довольно раздражающим.

Сделайте все свои файлы так, чтобы они могли быть построены с использованием только того, что они include. Если вам не нужно включать в свой заголовок, удалите его. В большом проекте, если вы не поддерживаете эту дисциплину, вы оставляете себя открытым для взлома всей сборки, когда кто-то удаляет включение из файла заголовка, который используется потребителем этого файла, а не даже заголовком.

Этот подход, который я разработал более чем через двадцать лет, заключается в следующем:

Рассмотрим библиотеку.

Существует несколько файлов C, один внутренний H-файл и один внешний H-файл. Файлы C include внутренний файл H. Внутренний файл H содержит внешний файл H.

Вы видите, что из компиляторов POV, когда он компилирует файл C, существует иерархия;

внешний -> внутренний -> код C

Это правильный порядок, так как внешний – это все, что третья сторона должна использовать в библиотеке. Для компиляции кода C требуется внутреннее.

Ваш исходный файл будет содержать инструкции include, если вы поместите его в заголовок. Однако в некоторых случаях было бы лучше помещать их в исходный файл.

Помните, что если вы включите этот заголовок в любые другие источники, они также получат входящие из заголовка, и это не всегда желательно. Вы должны включать только материал, где он используется.

Вы должны включать только файлы в своем заголовке, которые вам нужно объявить константы и объявления функций. Технически, они также будут включены в ваш исходный файл, но для большей ясности вы должны включать только в каждый файл файлы, которые вам действительно нужны. Вы также должны защитить их в своем заголовке от множественного включения:

 #ifndef NAME_OF_HEADER_H #define NAME_OF_HEADER_H ...definition of header file... #endif 

Это предотвращает включение заголовка несколько раз, что приводит к ошибке компилятора.