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

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

9 ответов


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

примеры:

  • ваша функция возвращает значение типа size_t. Тогда #include <stddef.h> на заголовок.
  • ваша функция использует strlen. Тогда #include <string.h> на источник.

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

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

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


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

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

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

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

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

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

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


в некоторых средах компиляция будет самой быстрой, если включать только нужные файлы заголовков. В других средах компиляция оптимизируется, если все исходные файлы могут использовать одну и ту же первичную коллекцию заголовков (некоторые файлы могут иметь дополнительные заголовки за пределами общего подмножества). В идеале заголовки должны быть построены так, чтобы несколько операций #include не имели никакого эффекта. Может быть полезно окружить операторы #include проверками для включаемого файла include-guard, хотя это создает зависимость от формата этого 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;

(Да, я знаю, что у меня будут проблемы, если я перейду на архитектуру big-endian, но поскольку мой компилятор не разрешает анонимные структуры в объединениях, использование именованных идентификаторов для байтов в объединении потребует, чтобы они были доступны как theUnion.б.Б1 так далее. что кажется довольно раздражающим.


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


подход, в который я эволюционировал более двадцати лет, таков;

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

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

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

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

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


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

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


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

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

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