Что размещать в заголовочном файле .h?

28/08/2013 - 19:40 Павел Бобков

Майкл Барр

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

   Один из таких навыков относится к созданию заголовочных файлов. Что нужно (или не нужно) размещать в заголовочном Си файле .h? Когда нужно создавать заголовочный файл? И почему? 

   На перечисленные вопросы у меня есть свой список ответов.

   Создавайте один заголовочный файл .h для каждого “модуля” системы. Модуль может содержать один или несколько компилируемых файлов (например, .с или .asm), но он должен реализовывать только один аспект системы. Примерами хорошо подобранных модулей являются: драйвер для АЦП; коммуникационный протокол, такой как FTP; менеджер аварий, который ведет журнал ошибок и предупреждает о них пользователя.

   Включайте в заголовочный файл .h все прототипы функций, которые составляют внешний интерфейс модуля. Например, заголовочный файл adc.h мог бы содержать прототипы функций adc_init(), adc_select_input(), adc_read().

   Не включайте в заголовочный файл функции и макросы, которые предназначены для использования внутри модуля. Желательно скрыть этих внутренних “помощников”, если они не используются в других модулях. (Если ваш модуль состоит из нескольких компилируемых файлов, которые используют эти внутренние функции, тогда создайте отдельный заголовочный файл для этих целей. ) Модуль А должен вызывать модуль B только через открытый интерфейс, определенный в заголовочном файле moduleb.h

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

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

   Не раскрывайте внутренний формат специфических структур данных, используемых интерфейсными функциями модуля. Другими словами, в заголовочном файле не должно быть никаких struct{…}foo. Если у вас есть тип данных, который нужно передать в или из модуля, определите типы данных в заголовочном файле через typedef. Например, так “typedef struct foo moduleb_type”. Клиентские модули не должны знать внутренний формат структур.

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

“What Belongs in a C .h Header File?” Michael Barr.
Вольный перевод ChipEnable.Ru

Comments   

# Степ 2015-02-05 06:07
на счет последнего пункта не понял, можно пожалуйста пример правильного и неправильного использования
# Pashgan 2015-02-25 17:03
Это такой хитрый паттерн, позволяющий скрывать детали реализации структуры. В комментариях к оригинальному посту есть объяснения и пример. Лучше почитать там.

У вас недостаточно прав для комментирования.