Что такое содержимое заголовочного файла
Перейти к содержимому

Что такое содержимое заголовочного файла

  • автор:

Содержимое заголовочного файла

В начале любой программы, как правило, подключается один из стандартных заголовочных файлов, поставляемых вместе с пакетом программ AVR Studio. Заголовочные файлы существуют для всех моделей AVR. По умолчанию исходной директорией их размещения является “С:\Program Files\Atmel\AVRTools\AvrAssembler2\Appnotes”. Каждый файл имеет имя подобное «m8def.inc», в котором первая часть указывает на модель микроконтроллера. Заголовочный файл содержит объявление всех символьных имен, которые могут быть использованы в программе. В первую очередь к ним относятся названия управляющих РВВ и их битовых полей:

; ***** I/O REGISTER DEFINITIONS ************************** ; NOTE: ; Definitions marked "MEMORY MAPPED" are extended I/O ports ; and cannot be used with IN/OUT instructions .equ SREG = 0x3F .equ SPL = 0x3D .equ SPH = 0x3E . .equ TWAR = 0x02 .equ TWSR = 0x01 .equ TWBR = 0x00 ; ***** BIT DEFINITIONS *********************************** . ; ***** CPU ************************** ; SREG - Status Register .equ SREG_C = 0 ; Carry Flag .equ SREG_Z = 1 ; Zero Flag .equ SREG_N = 2 ; Negative Flag .equ SREG_V = 3 ; Two's Complement Overflow Flag .equ SREG_S = 4 ; Sign Bit .equ SREG_H = 5 ; Half Carry Flag .equ SREG_T = 6 ; Bit Copy Storage .equ SREG_I = 7 ; Global Interrupt Enable .

Директива .equ связывает название каждого из РВВ с его фактическим адресом в адресном пространстве ввода-вывода. Эти адреса могут использоваться только совместно с инструкциями in, out, sbi, cbi, sbis, sbic. Если обращаться к управляющему регистру как к ячейке памяти в абсолютном адресном пространстве, то к его имени необходимо будет добавлять смещение 0x20:
lds SPL+0x20,R16 ;копировать R16 в ячейку 0x5D SRAM (0x3D I/O). Обозначения РВВ и их битов в точности совпадают с теми, которые приводятся в технической документации на каждую конкретную модель. Поэтому не рекомендуется изменять эти имена в заголовочном файле по своему усмотрению. Стандартные названия помогают облегчить переносимость программ в пределах одного семейства. Если, например, понадобиться перенести рабочий код с модели ATmega8 на ATmeg8535, то все что надо будет сделать – это заменить в исходном тексте файле «m8def.inc» на «m8535def.inc» и заново откомпилировать проект. В каждом заголовочном файле РОНам R27…R31 директивой .def присвоены дополнительные служебные имена:

; ***** CPU REGISTER DEFINITIONS ************************** .def XH = R27 .def XL = R26 .def YH = R29 .def YL = R28 .def ZH = R31 .def ZL = R30

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

; ***** DATA MEMORY DECLARATIONS ************************** .equ FLASHEND = 0x0FFF ; Note: Word address .equ IOEND = 0x003F .equ SRAM_START = 0x0060 .equ SRAM_SIZE = 1024 .equ RAMEND = 0x045F .equ XRAMEND = 0x0000 .equ E2END = 0x01FF .equ EEPROMEND = 0x01FF .equ EEADRBITS = 9 ̣̣̣̣̣̣̣̣ ; ***** BOOTLOADER DECLARATIONS *************************** .equ NRWW_START_ADDR = 0xC00 .equ NRWW_STOP_ADDR = 0xFFF .equ RWW_START_ADDR = 0x0 .equ RWW_STOP_ADDR = 0xBFF .equ PAGESIZE = 32 .equ FIRSTBOOTSTART = 0xF80 .equ SECONDBOOTSTART = 0xF00 .equ THIRDBOOTSTART = 0xE00 .equ FOURTHBOOTSTART = 0xC00 .equ SMALLBOOTSTART = FIRSTBOOTSTART .equ LARGEBOOTSTART = FOURTHBOOTSTART

В конце файла приведена таблица векторов прерывания:

; ***** INTERRUPT VECTORS ********************************* .equ INT0addr = 0x0001 ; External Interrupt Request 0 .equ INT1addr = 0x0002 ; External Interrupt Request 1 .equ OC2addr = 0x0003 ; Timer/Counter2 Compare Match ̣̣̣̣̣̣̣̣ .equ ACIaddr = 0x0010 ; Analog Comparator .equ TWIaddr = 0x0011 ; 2-wire Serial Interface .equ SPMRaddr = 0x0012 ; Store Program Memory Ready .equ INT_VECTORS_SIZE = 19 ; size in words

Перейти к следующей части: Структура программы

Теги:

Котов Игорь Юрьевич Опубликована: 2012 г. 0 0

Вознаградить Я собрал 0 0

Оценить статью

  • Техническая грамотность

include и заголовочный файлы

Заголовочный файл (иногда головной файл, англ. header file), или подключаемый файл — в языках программирования Си и C++ файл, содержащий определения типов данных, структуры, прототипы функций, перечисления, макросы препроцессора. Имеет по умолчанию расширение .h; иногда для заголовочных файлов языка C++ используют расширение .hpp. Заголовочный файл используется путём включения его текста в данный файл директивой препроцессора #include. Заголовочный файл в общем случае может содержать любые конструкции языка программирования, но на практике исполняемый код (за исключением inline-функций в C++) в заголовочные файлы не помещают. Например, идентификаторы, которые должны быть объявлены более чем в одном файле, удобно описать в заголовочном файле, а затем его подключать по мере надобности.

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

По традиции заголовочные файлы имеют расширение .h, а файлы, содержащие определения функций или данных, расширение .c. Иногда их называют «h-файлы» или «с-файлы» соответственно. Используют и другие расширения для этих файлов: .C, cxx, .cpp и .cc. Принятое расширение вы найдете в своем справочном руководстве.

Для включения файлов из стандартных каталогов (обычно каталоги с именем INCLUDE) надо вместо кавычек использовать угловые скобки < и >. Если имя_файла — в угловых скобках, то препроцессор разыскивает файл в стандартных системных каталогах. Если имя_файла заключено в кавычки, то вначале препроцессор просматривает текущий каталог пользователя и только затем обращается к просмотру стандартных системных каталогов. Например:

#include // включение из стандартного каталога. Имя в угловых скобках. #include "myheader.h" // включение из текущего каталога. Имя в кавычках. #include "..\CommonFiles\CmnHdr.h"

Включение из стандартных каталогов имеет то преимущество, что имена этих каталогов никак не связаны с конкретной программой (обычно вначале включаемые файлы ищутся в каталоге /usr/include/CC, а затем в /usr/include). К сожалению, в этой команде пробелы существенны:

#include < stream.h>// не будет найден

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

Укажем, что может содержать заголовочный файл:

Определения типов struct point < int x, y; >; Шаблоны типов template class V < /* . */ >Описания функций extern int strlen(const char*); Определения inline char get() < return *p++; >функций-подстановок Описания данных extern int a; Определения констант const float pi = 3.141593; Перечисления enum bool < false, true >; Описания имен class Matrix; Команды включения файлов #include Макроопределения #define Case break;case Комментарии /* проверка на конец файла */

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

Определений обычных функций char get() < return *p++; >Определений данных int a; Определений составных const tb[i] = < /* . */ >; констант

Один Заголовочный Файл (.h)

Проще всего решить проблему разбиения программы на несколько файлов поместив функции и определения данных в подходящее число исходных файлов и описав типы, необходимые для их взаимодействия, в одном заголовочном файле, который включается во все остальные файлы. Для программы калькулятора можно использовать четыре .c файла: lex.c, syn.c, table.c и main.c, и заголовочный файл dc.h, содержащий описания всех имен, которые используются более чем в одном .c файле.

Множественные Заголовочные Файлы (.h)

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

Заголовочные файлы и файлы реализации

Все примеры, которые я использовал до этого, использовали только один файл, который компилировался через g++ . Несмотря на то, что большие программы можно написать в одном файле и скомпилировать его, такие программы редко бывают удобны для последующей работы. Код, написанный в одном многометровом файле запутывает и усложняет т.н. «рефакторинг».

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

С заголовными файлами мы уже встречались, когда подключали, например, библиотеки для работы со строками следующим образом: #include . Напомню, что препроцессорная директива #include имеет 2 способа указания на внешние заголовные файлы: с помощью треугольных скобок (<>) и с помощью кавычек («»). Принципиальное различие состоит в том, что при указании <> компилятор будет пытаться искать заголовочные файлы в библиотеках, которые указаны в ОС как источники библиотек C++. При разработке собственных файлов необходимо использовать кавычки; они указывают компилятору, что заголовочный файл нужно искать относительно той директории, в которой находится компилируем исходный файл.

Возьмём последний пример из статьи про классы.

#include #include using namespace std; class Person < string lastname; string firstname; string middlename; public: Person(string lastname, string firstname, string middlename) : lastname(lastname), firstname(firstname), middlename(middlename) < cout friend ostream& operator; ostream& operator<<(ostream& os, Person &p) < os int main(int argc, char *argv[])

Вынесем класс в заголвочный файл, у нас получится следующее:

#include using namespace std; class Person < string lastname; string firstname; string middlename; public: Person(string lastname, string firstname, string middlename) : lastname(lastname), firstname(firstname), middlename(middlename) < cout friend ostream& operator<< (ostream &out, Person &p); >; ostream& operator<<(ostream& os, Person &p)
#include #include «person.h» int main(int argc, char const *argv[])

Собираем проект g++ main.cpp и получим точно такую же программу, как если бы сборка происходила из одного и того же файла. Это всё из-за того, что препроцессорная директива #include ищет нужные нам заголовочные файлы и включает их в тело программы. Таким образом нам удалось вынести целую сущность в отдельный файл! Теперь же необходимо избавиться от реализации в заголовчном файле. Это мотивировано тем, что некоторые программы могут быть исопльзованы сторонними людьми, поэтому они должны подключать заголовчный файл, наподобие того, как подключается файл со строками, а вот реализация для посторонних должна быть скрыта. Такие файлы комплириуются в статические или динамические библиотеки (последние знакомы всем жителями ОС Виндоус как .dll), но объяснение этих нюансов выходит за рамки этой статьи.

Выносим реализацию в отдельный файл и получаем проект из 3-х файлов (main.cpp не изменяется):

#include #include using namespace std; #ifndef PERSON_H #define PERSON_H class Person < string lastname; string firstname; string middlename; public: Person(string lastname, string firstname, string middlename); friend ostream& operator; #endif

#include #include #include "person.h" using namespace std; Person::Person(string lastname, string firstname, string middlename) : lastname(lastname), firstname(firstname), middlename(middlename) < cout ostream& operator

При реализации класса нам обязательно указывать название функции так: [Возвращаемое значеие] [Имя класса]::[Имя функции и аргументы] .

#ifndef PERSON_H , #define PERSON_H и #endif нужны для того, чтобы заголовончый файл при компиляции был включён в тело программы только 1 раз. Эти директивы прочитываются следующим образом: если не определён [название], тогда определить [название] и закончить проверку. Без этих директив, компилятор попытается подключить один и тот же заголовочный файл столько раз, сколько файлов реализаций его подключают; в нашем случае, это будет 2 раза: из файла main.cpp и файла person.cpp.

Для того, чтобы программа скомпилировалась, g++ нужно передавать в качестве аргументов оба файла реализации: g++ main.cpp person.cpp . Кстати, на этом этапе вы можете попытаться создать библиотеку person, скомпилированную в объектный файл. Поступим следующим образом:

g++ -c person.cpp g++ main.cpp person.o

Вы можете передать другому человеку только скомпилированный файл person.o и заголовочный файл person.h, и он сможет написать main.cpp и собрать программу (при условии, что у вас один и тот же компилятор)! Встаёт вопрос, как обеспечить сборку проекта из тысячи файлов? Не перечислять же их в аргументах, в самом деле. Для этих целей используются системы автоматической сборки, одной из которых является способ сборки с makefile. Прочитайте статью Makefile для самых маленьких, чтобы научится это делать.

Заголовочный файл

Заголовочный файл (иногда головной файл, англ. header file ), или подключаемый файл — в языках программирования файл, механически «вставляемый» компилятором в исходный текст в том месте, где располагается некоторая директива ( в Паскале, #include в Си).

В языках программирования Си и C++, заголовочные файлы — основной способ подключить к программе типы данных, структуры, прототипы функций, перечислимые типы, и макросы, используемые в другом модуле. Имеет по умолчанию расширение .h; иногда для заголовочных файлов языка C++ используют расширение .hpp. Чтобы избежать повторного включения одного и того же кода, используются директивы #ifndef, #define, #endif . Заголовочный файл в общем случае может содержать любые конструкции языка программирования, но на практике исполняемый код (за исключением inline-функций в C++) в заголовочные файлы не помещают. Например, идентификаторы, которые должны быть объявлены более чем в одном файле, удобно описать в заголовочном файле, а затем его подключать по мере надобности. Подобным же образом работает модульность и в большинстве ассемблеров.

По сложившейся традиции, в заголовочных файлах объявляют функции стандартной библиотеки Си и Си++.

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

Назначение

В современных языках программирования программы составляются из модулей, компилируемых по отдельности. В связи с этим возникает вопрос: как указать, что подпрограмма или переменная X определена в модуле Y ? Для этого существует несколько решений, в Си применено такое.

В одной из единиц компиляции (то есть с -файле) описывается функция, например:

int add(int a, int b)  return a + b; > 

Чтобы на неё можно было ссылаться из других единиц компиляции, требуется объявить её при помощи прототипа функции, то есть:

int add(int, int); int triple(int x)  return add(x, add(x, x)); > 

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

Заголовочный файл является одним из решений этой проблемы. В заголовочном файле модуля объявляется каждая функция, объект и тип данных, являющиеся частью интерфейса вызова модуля — например, в этом случае заголовочный файл может содержать только объявление функции add . Каждый исходный файл, ссылающийся на функцию add , должен использовать директиву #include для подключения заголовочного файла:

/* File triple.c */ #include "add.h" int triple(int x)  return add(x, add(x, x)); > 

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

/* File add.h */ #ifndef ADD_H #define ADD_H int add(int, int); #endif /* ADD_H */ 

Кроме конструкции #ifndef - #endif иногда применяется нестандартная #pragma once :

/* File add.h */ #pragma once int add(int, int); 

Заголовочные файлы облегчают поддержку — при изменении определения должно быть обновлено лишь одно объявление (то, которое находится в заголовочном файле). К исходному файлу также можно подключать заголовочный файл, содержащий определение, используемые в исходниках. Это позволяет компилятору сверять, совпадает ли объявление в h -файле с определением в c -файле:

/* File add.c */ #include "add.h" int add(int a, int b)  return a + b; > 

Обычно заголовочные файлы применяются только для более чёткого определения интерфейса и обычно содержат комментарии, поясняющие способы использования компонентов, объявленных в файле. В приведённом примере использованные подпрограммы выделены в отдельные исходные файлы, которые должны компилироваться отдельно (исключением в языках Си и C++ являются встраиваемые функции, которые зачастую включаются в заголовочный файл из-за того, что в большинстве случаев использования не получается правильно раскрыть встраиваемую функцию без обращений к их определению во время компиляции).

Сравнение с прямым получением заголовков из откомпилированного модуля

Альтернатива заголовочным файлам — получение информации об объявленных типах, функциях и т. д. напрямую из откомпилированного модуля. Так поступают языки Паскаль, Java и другие.

Преимущества

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

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

// unit.h #ifndef __UNIT_H__ #define __UNIT_H__ #ifndef UNIT_STL_UNUSED #include void dump(std::ostream& os); void dump()  dump(std::cout); > #endif void run(); #endif 
// main.cpp #define UNIT_STL_UNUSED #include "unit.h" int main()  run(); return 0; > 

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

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

Заголовочный файл позволяет задать то, что невозможно задать с помощью модулей — подстановки с помощью #define , директивы компилятора, незаконченные синтаксические конструкции…

Недостатки

Заголовочные файлы намного медленнее — чтобы откомпилировать 10 c -файлов, к каждому из которых подключён длинный h -файл, компилятору придётся пройти по заголовку 10 раз. Чтобы справиться с этой проблемой, во многих компиляторах используют предварительно откомпилированные заголовки.

Заголовочные файлы вместе с некоторыми объектами языка C++ (константы, inline -функции, шаблоны, static -переменные) образуют тяжеловесные конструкции.

Если вдруг программист изменил c -файл, забыв сделать то же с h -файлом, компоновщик выдаст расплывчатое сообщение об ошибке без номера строки. Особенно это заметно в C++, где одна и та же функция может иметь разный набор аргументов, и проверка на уровне компилятора не срабатывает. Если программист случайно оставит конструкцию в h -файле незаконченной, ошибка будет совсем в другом c - или h -файле.

В некоторых языках (например, Java) вообще не требуется изменять код одновременно в двух местах.

См. также

  • Стандартная библиотека языка Си — описывает стандартные заголовочные файлы языка Си
  • Стандартная библиотека языка C++ — описывает стандартные заголовочные файлы языка Си++

Ссылки

Литература

  • Подбельский В. В. Глава 8. Препроцессорные средства // Язык Си++ / рец. Дадаев Ю. Г.. — 4. — М .: Финансы и статистика, 2003. — С. 263-280. — 560 с. — ISBN 5-279-02204-7, УДК 004.438Си(075.8) ББК 32.973.26-018 1я173
  • Концепции языков программирования
  • Язык программирования Си

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *