В некоторых средах компиляция будет самой быстрой, если в нее будут включены только нужные файлы заголовков. В других средах компиляция будет оптимизирована, если все исходные файлы могут использовать одну и ту же основную коллекцию заголовков (некоторые файлы могут иметь дополнительные заголовки помимо общего подмножества). В идеале заголовки должны быть созданы так, чтобы несколько операций #include не имели никакого эффекта. Было бы неплохо окружить операторы #include проверками include-guard для включаемого файла, хотя это создает зависимость от формата этого средства защиты. Кроме того, в зависимости от поведения кэширования файлов в системе, необязательный #include, цель которого оказывается полностью удаленным # ifdef, может не занять много времени.
Также следует учитывать, что если функция принимает указатель на структуру, можно записать прототип как
void foo (struct BAR_s * bar);
без определения того, что BAR_s должны быть в области видимости. Очень удобный подход, позволяющий избежать ненужных включений.
PS - во многих моих проектах будет файл, который, как ожидается, каждый модуль будет #include, содержащий такие вещи, как typedef для целочисленных размеров и несколько общих структур и объединений [например,
typedef union {
беззнаковое длинное l;
беззнаковый короткий lw [2];
беззнаковый символ lb [4];
} U_QUAD;
(Да, я знаю, что у меня будут проблемы, если я перейду на архитектуру с прямым порядком байтов, но поскольку мой компилятор не разрешает анонимные структуры в объединениях, использование именованных идентификаторов для байтов в объединении потребует доступа к ним как theUnion.b.b1 и т.д., что кажется довольно раздражающим.