Что такое заголовочные файлы

Основы языка Си: структура Си-программы, базовые типы и конструирование новых типов, операции и выражения

Аннотация: Лекция посвящена введению в язык Си. Объясняются общие принципы построения Си-программы: разбиение проекта на h- и c-файлы, т.е. разделение интерфейса и реализации, использование препроцессора. Приводятся базовые типы языка Си, конструкции массива и указателя, позволяющие строить новые типы, а также модификаторы типов. Рассматриваются всевозможные операции и выражения языка Си.

Основы языка Си

В настоящее время язык Си и объектно-ориентированные языки его группы (прежде всего C++, а также Java и C#) являются основными в практическом программировании. Достоинство языка Си — это, прежде всего, его простота и лаконичность. Язык Си легко учится. Главные понятия языка Си , такие, как статические и локальные переменные, массивы, указатели, функции и т.д., максимально приближены к архитектуре реальных компьютеров. Так, указатель — это просто адрес памяти, массив — непрерывная область памяти, локальные переменные — это переменные, расположенные в аппаратном стеке, статические — в статической памяти. Программист, пишущий на Си , всегда достаточно точно представляет себе, как созданная им программа будет работать на любой конкретной архитектуре. Другими словами, язык Си предоставляет программисту полный контроль над компьютером.

Первоначально язык Си задумывался как заменитель Ассемблера для написания операционных систем. Поскольку Си — это язык высокого уровня, не зависящий от конкретной архитектуры, текст операционной системы оказывался легко переносимым с одной платформы на другую. Первой операционной системой, написанной практически целиком на Си , была система Unix. В настоящее время почти все используемые операционные системы написаны на Си . Кроме того, средства программирования, которые операционная система предоставляет разработчикам прикладных программ (так называемый API — Application Program Interface ), — это наборы системных функций на языке Си .

Тем не менее, область применения языка Си не ограничилась разработкой операционных систем. Язык Си оказался очень удобен в программах обработки текстов и изображений, в научных и инженерных расчетах. Объектно-ориентированные языки на основе Си отлично подходят для программирования в оконных средах.

В данном разделе будут приведены лишь основные понятия языка Си (и частично C++). Это не заменяет чтения полного учебника по Си или C++, например, книг [6] и [8].

Мы будем использовать компилятор C++ вместо Cи. Дело в том, что язык Си почти целиком входит в C++, т.е. нормальная программа , написанная на Си , является корректной C++ программой. Слово «нормальная» означает, что она не содержит неудачных конструкций, оставшихся от ранних версий Си и не используемых в настоящее время. Компилятор C++ предпочтительнее, чем компилятор Си , т.к. он имеет более строгий контроль ошибок. Кроме того, некоторые конструкции C++, не связанные с объектно-ориентированным программированием, очень удобны и фактически являются улучшением языка Си . Это, прежде всего, комментарии // , возможность описывать локальные переменные в любой точке программы, а не только в начале блока, и также задание констант без использования оператора #define препроцесора. Мы будем использовать эти возможности C++, оставаясь по существу в рамках языка Си .

Структура Си-программы

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

В языке Си исходные файлы бывают двух типов:

  • заголовочные, или h-файлы;
  • файлы реализации, или Cи-файлы.

Имена заголовочных файлов имеют расширение » .h «. Имена файлов реализации имеют расширения » .c » для языка Си и » .cpp «, » .cxx » или » .cc » для языка C++.

К сожалению, в отличие от языка Си , программисты не сумели договориться о едином расширении имен для файлов, содержащих программы на C++. Мы будем использовать расширение » .h » для заголовочных файлов и расширение » .cpp » для файлов реализации.

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

определяющая целочисленную переменную x , является ошибкой. Вместо этого следует использовать описание

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

Файлы реализации, или Cи-файлы, содержат тексты функций и определения глобальных переменных. Говоря упрощенно, Си -файлы содержат сами программы, а h-файлы — лишь информацию о программах.

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

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

(stdio — от слов standard input /output). Имя h-файла записывается в угловых скобках, если этот h- файл является частью стандартной Си -библиотеки и расположен в одном из системных каталогов. Имена h-файлов, созданных самим программистом в рамках разрабатываемого проекта и расположенных в текущем каталоге, указываются в двойных кавычках, например,

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

задает символическое имя PI для константы 3.14159265 . После этого имя PI можно использовать вместо числового значения. Препроцессор находит все вхождения слова PI в текст и заменяет их на константу. Таким образом, препроцессор осуществляет подмену одного текста другим. Необходимость использования препроцессора всегда свидетельствует о недостаточной выразительности языка. Так, в любом Ассемблере средства препроцессирования используются довольно интенсивно. В Си по возможности следует избегать чрезмерного увлечения командами препроцессора — это затрудняет понимание текста программы и зачастую ведет к трудно исправляемым ошибкам. В C++ можно обойтись без использования директив #define для задания констант. Например, в C++ константу PI можно задать с помощью нормального описания

Это является одним из аргументов в пользу применения компилятора C++ вместо Си даже при трансляции программ, не содержащих конструкции класса.

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

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

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

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

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

Преимущества использования

В большинстве современных языков программирования, корректность функционирования программ зависит от ее собственных компонентов (подпрограмм), а эти компоненты могут распространяться в виде файлов, компилируемых отдельно. Если подпрограмму необходимо использовать где-то еще, где она не определена, возникает необходимость в использовании предварительного объявления или прототипа функции. Например, функция определена следующим образом в одном исходном файле:

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

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

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

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

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

Альтернативные варианты

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

C++/Header file

Заголовочный файл (англ. header file , или подключаемый файл) в C++ — файл, содержимое которого автоматически добавляется препроцессором в исходный текст в том месте, где располагается некоторая директива препроцессора (в C/C++ – #include , т.о. подкл. файла делается так: #include ).

Содержание

Преамбула

В ЯП C/C++ заголовочные файлы — основной способ подключить к программе типы данных, структуры, прототипы функций, перечислимые типы и макросы, используемые в др. модуле.

Расширения заголовочный файлов

По-умолч. используется расширение «.h»; иногда для заголовочных файлов языка C++ используют расширение «.hpp». Позже было принято, что заголовочные файлы всех стандартных библиотек языка C++ пишутся без расширений. Ранее им давались самые различные расширения: «.h», «.h++», «.hpp», «.hxx», «.hh». Неродным (пользовательским, сторонним) заголовочным файлам принято по-прежнему давать расширение «.h».

Общие сведения

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

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

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

Назначение

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

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

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

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

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

Чтобы избежать повторного включения одного и того же кода, используются директивы #ifndef, #define, #endif .

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

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

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

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

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

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

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

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

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

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

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

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

Упрощается взаимодействие между модулями, написанными на разных языках. Компилятору и компоновщику вообще безразлично, написан вызываемый модуль на том же языке или на другом. К тому же разные языки могут скомпилировать свои модули в одинаковые объектные файлы — в таком случае получается один компоновщик на несколько языков. Точно так же просто сделать библиотеку, которая по выбору пользователя включается в проект в виде CPP-файлов, хранится заранее откомпилированной и прикомпоновывается статически, или прикомпоновывается как DLL.

Недостатки

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

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

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

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

Разница между файлом заголовка и файлом библиотеки

Видео: Разница между файлом заголовка и файлом библиотеки | Сравните разницу между похожими терминами

Содержание:

Ключевое отличие — заголовок Файл против файла библиотеки

У языков программирования, таких как C и C ++, есть файлы заголовков и файлы библиотеки. Эти языки хранят константы и прототипы функций в файлах заголовков. Программист может написать файл заголовка самостоятельно или они идут вместе с компилятором. Заголовочные файлы полезны, поскольку они делают программу более организованной и управляемой. Если все определенные функции находятся в одном файле, это усложняет программу. Следовательно, программист может включить требуемый заголовочный файл при написании программы. Заголовочный файл состоит из объявлений функций. Эти объявления сообщают компилятору имя функции, тип возвращаемого значения и параметры. Файл библиотеки содержит фактическую реализацию функции, объявленной в файле заголовка. Библиотека C и библиотека C ++ — это файлы библиотеки. Следовательно ключевое отличие между файлом заголовка и файлом библиотеки заключается в том, что файл заголовка содержит объявления функций, которые будут совместно использоваться несколькими исходными файлами, а файл библиотеки — это файл, содержащий определение функции для объявленных функций в файле заголовка.

1. Обзор и основные отличия
2. Что такое заголовочный файл
3. Что такое файл библиотеки
4. Сходства между файлом заголовка и файлом библиотеки
5. Параллельное сравнение — файл заголовка и файл библиотеки в табличной форме
6. Резюме

Что такое заголовочный файл?

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

В Заголовочный файл содержит функции, определенные для ввода и вывода. Fclose используется для закрытия потока. Printf используется для отправки форматированного вывода на стандартный вывод. Fscanf используется для чтения форматированного ввода из стандартного ввода. В Заголовочный файл содержит функции, относящиеся к консоли. Getch используется для чтения символа из консоли. Заголовочный файл содержит функции, относящиеся к обработке строк. Strlen — найти длину строки. Функция strcmp предназначена для сравнения двух строк.

Функции, необходимые для программирования графики, включены в заголовочный файл. В Заголовочный файл содержит операции, связанные с математикой. Ранд используется для создания случайного числа. Функция pow используется для нахождения степени числа. Некоторые другие математические функции: sin, cos, tan, sqrt. Эти функции уже объявлены в файлах заголовков.

Включение файлов заголовков в C ++ также похоже на C. Здесь также используются директивы препроцессора. Синтаксис добавления файла заголовка в C ++ — #include . Если программист хочет включить файл заголовка iostream, это делается с помощью #include . Это стандартная библиотека потоков ввода-вывода. CIN — это стандартный поток ввода. Cout предназначен для стандартного потока вывода.

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

Что такое файл библиотеки?

Файл библиотеки будет содержать определения функций для объявленных функций в файле заголовка. Определения функций — это фактическая реализация функции. Программист использует в программе функции, объявленные в файлах заголовков. Нет необходимости реализовывать их с самого начала. При компиляции программы компилятор находит определения в файле библиотеки для объявленных функций в файле заголовка.

Несмотря на то, что файлы заголовков включены в программу программистом, связанные файлы библиотеки обнаруживаются компилятором автоматически. Следовательно, компилятор использует файлы библиотеки для поиска фактических реализаций объявленных функций в файлах заголовков. Если в программе используется функция printf (), определение того, как она работает, находится в соответствующем файле библиотеки. Если math.h — файл заголовка, math.lib — файл библиотеки.

В чем сходство между файлом заголовка и файлом библиотеки?

  • Оба они используются в языке C / C ++.

В чем разница между файлом заголовка и файлом библиотеки?

Файл заголовка и файл библиотеки

Сводка — заголовок Файл против файла библиотеки

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

Скачать PDF-версию файла заголовка и файла библиотеки

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

Как происходит компиляция

Напомним, что на прошлой практике был создан проект, состоящий из трёх файлов:

  • 1.cpp — main (вызывает foo() )
  • 2.cpp — foo — определение
  • 2.h — foo — объявление

Заметим, что этот же код можно было написать, без использования 2.h .

Возникает законный вопрос: зачем был создан файл 2.h ?

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

Защита от повторного включения заголовочных файлов

Иногда может возникнуть ситуация, когда один заголовочный файл включён не один, а несколько раз. Посмотрим, к каким проблемам это может привести. Если в файле содержались только объявления (как в файле 2.h из примера), то ничего страшного не произойдёт (стол останется столом, сколько об этом не объявляй), но не всегда всё так удачно. Действительно, ведь заголовочный файл кроме объявлений файлов может содержать и определения (например констант или классов). Тогда его повторное включение приведет к ошибке компоновки. Чтобы этого избежать, все заголовочные файлы следует защищать от повторного включения.

Делается это так:

Здесь директива #ifndef указывает препроцессору, что участок кода до #endif следует компилировать только в случае, если объявления _2_H_ не было. Директива #define же указывает, что _2_H_ следует объявить.

Допустим, что есть файл 3.cpp .

Что произойдет при препроцессинге этого файла?

Вместо каждого #include «2.h» будет подставлено содержимое соответствующего файла. На момент первой подстановки _2_H_ еще не определено, по этому произойдут подстановка объявления функции и объявление _2_H_ . На момент же, когда препроцессор перейдет ко второму включению, _2_H_ уже определено, и потому подстановка выполнена не будет.

Так как для совершение столь распространенного действия приходится писать целых три директивы, а к тому же следить за уникальностью объявляемых констант — была придумана директива #pragma once , которая, будучи помещенной в начало заголовочного файла , позволяет добиться того же результата. Однако пользоваться ей надо осторожно, так как в стандарт она не вошла, и потому поддерживается не всеми компиляторами (gcc и компилятор компании Microsoft — поддерживают).

Подробнее о препроцессоре

Препроцессор — весьма мощное средство, и в языке C он использовался весьма широко.

Возьмем для примера функцию, вычисляющую максимум:

В языке C нет перегрузки функций.

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

  • Происходит текстовая подстановка в макрос. (Результат работы программы будет 2, а не 1)
  • В случае если не все параметры заключены в скобки, возможны неожиданности с приоритетами операций.

Сборка

Вспомним, как происходит процесс сборки:

Из этой картинки хорошо видно, зачем нужен Makefile . Процесс сборки весьма громоздок, причем большая часть процесса проделывается для каждого файла независимо от других. Значит, если проделывать каждый шаг лишь для тех файлов, для которых это необходимо (зависимости которых обновились) сборка значительно ускорится. (а для больших проектов, например OpenOffice, она может занимать несколько часов)

Многие компиляторы умеют сами обнаруживать зависимости и создавать Makefile .

Библиотеки

Какие бывают библиотеки?

  • Статические ( *.a , *.lib ) — код функций вставляется в исполняемый файл
  • Динамические ( *.so , *.dll ) — в исполняемый файл вставляется имя функции и ее адрес в библиотеке. Для работы необходим файл библиотеки

Отличие библиотек от программ — нет точки входа int main() .

Пример подключения библиотек:

g++ это синоним для gcc -lstd++ , что указывает, что нужно линковать со стандартной библиотекой с++. По умолчанию приложение собирается с динамическими библиотеками, в случае, если требуется собрать со статическими необходимо явно указать ключ -static . Кстати, как уже говорилось, gcc — это огромный конвейер, содержащий несколько различных компиляторов, и вызов g++ ничего не говорит, о языке написанной нами программы. Информацию о том, какой компилятор следует использовать gcc получает из расширения файла.

Упражнение 1. Попробовать собрать статическую/динамическую библиотеку и использовать ее в программе.

Подробнее об объектных файлах

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

Упражнение 2. Посмотреть содержимое объектного файла.

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

Почему компилятор переименовывает функции?

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

Так как в объектном файле информации о языке уже не сохраняется (в том числе о типах передаваемых/возвращаемых значений), то для того, чтобы эти функции различать (иначе бы при линковке возникла ошибка повторного определения) компилятор дописывает к названиям функций информацию о типах (этот процесс называется маскированием). Восстановить объявление функции по ее расширенному названию можно, например, с помощью программы c++filt.

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

Языки программирования

Разделение на файлы с исходным кодом и заголовочные файлы чисто условное, нет правил, запрещающих использовать .cpp файл как заголовочный, однако мы не рекомендуем так делать — использование общепринятых правил именования файлов упростит жизнь вам и вашим коллегам.

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

Объявления и определения

Объявление (declaration) — вводит имя, возможно, не определяя деталей. Например, ниже перечислены объявления:

  • int a; — объявление переменной типа int,
  • void foo(); — объявление функции с именем foo,
  • void bar() < foo(); >— объявление функции с именем bar.

Определение (definition) — это объявление, дополнительно определяющее детали, необходимые компилятору. Из перечисленных выше объявлений, определениями являются только два:

  • int a; — объявление переменной типа int,
  • void bar() < foo(); >— объявление функции вместе с телом является определением.

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

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

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

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

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

Т.к. это два разных файла, то их компиляция будет проходить по отдельности. И когда компилятор будет читать файл foo.cpp, то встретив вызов функции bar, он не будет знать, что это функция определена в каком-то другом файла.

Предположим, что мы изменили функцию bar. Следующий код некорректен — объявление отличается от определения. Данный код скомпилируется, но его поведение неопределенно.

Предположим, что функция bar используется не в одном файле, а в нескольких, тогда придётся исправлять объявления функции bar в этих файлах, в каждом по отдельности. Для того чтобы локализовать изменения используются заголовочные файлы. В заголовочный файл выносится объявление функции и во всех местах, где функция используется, просто подключается соответствующий заголовочный файл.
Файл foo.cpp:

Может случиться двойное включение заголовочного файла.
Файл foo.cpp:

Это можно исправить двумя способами:
1) Наиболее переносимо через «стражи включения». Файл bar.hpp:

Например, есть проект, в котором есть три файла с различными определениями: utility.cpp, lexer.cpp и parser.cpp. Каждому файлу соответствует заголовочный файл с необходимыми объявлениями: utility.hpp, lexer.hpp и parser.hpp. Каждый из файлов определений непосредственно (т. е. с помощью директивы include) подключает соответствующий ему заголовочный файл с объявлениями (т. е. utility.cpp подключает utility.hpp, а lexer.cpp подключает lexer.hpp и аналогично для parser.cpp и parser.hpp). Кроме того, файл parser.hpp непосредственно подключает заголовки lexer.hpp и utility.hpp, а файл lexer.hpp подключает заголовок utility.hpp.

Добавление в проект заголовочного файла

Помимо файлов с исходным кодом (в языке C++ они имеют расширение cpp , а в языке C — расширение c ) в проекте могут быть заголовочные файлы (в языке C++ они имеют расширение hpp или h ). В заголовочных файлах указываются прототипы функций и различные объявления.

Для создания заголовочного файла в окне Проекты щелкаем правой кнопкой мыши на названии проекта и из контекстного меню выбираем пункт Добавить новый. В открывшемся окне (рис. 2.7) из списка слева выбираем пункт C/C++, а из списка справа — пункт Заголовочный файл C/C++. Нажимаем кнопку Выбрать. На следующем шаге (рис. 2.8) вводим название HelloWorld.hpp в поле Имя файла. В поле Путь указываем значение C:cppprojectsQtHelloWorld . Нажимаем кнопку Далее. На следующем шаге (рис. 2.9) нажимаем кнопку Завершить.

2_7.png

Рис. 2.7. Создание заголовочного файла. Шаг 1

2_8.png

Рис. 2.8. Создание заголовочного файла. Шаг 2

2_9.png

Рис. 2.9. Создание заголовочного файла. Шаг 3

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

Листинг 2.2. Содержимое файла HelloWorld.hpp

Текст после символов // является комментарием. Инструкции, начинающиеся с символа # , — это директивы препроцессора. В нашем примере их три:

  • #ifndef — проверяет отсутствие константы с именем HELLOWORLD_HPP ;
  • #define — создает константу с именем HELLOWORLD_HPP ;
  • #endif — обозначает конец блока проверки отсутствия константы.

Зачем нужны эти директивы препроцессора? Заголовочный файл мы подключаем к файлу с исходным кодом с помощью директивы #include :

Встретив в исходном коде директиву #include компилятор вставляет все содержимое заголовочного файла на место директивы. Если мы вставим две одинаковые директивы #include , то содержимое заголовочного файла будет вставлено дважды. Так как объявить один идентификатор (например, глобальную переменную) дважды нельзя, компилятор выведет сообщение об ошибке. Чтобы этого избежать, прототипы функций и прочие объявления вкладываются в блок, ограниченный директивами #ifndef и #endif . В директиве #ifndef указывается константа, совпадающая с названием заголовочного файла. Все буквы в имени константы заглавные, а точка заменена символом подчеркивания. Если константа не существует (при первом включении заголовочного файла так и будет), то с помощью директивы #define эта константа создается и содержимое блока вставляется в исходный код. При повторном включении заголовочного файла константа уже существует, поэтому содержимое блока будет проигнорировано. Таким образом заголовочный файл дважды вставлен не будет, а значит и ошибки не будет.

Вместо этих директив можно указать в самом начале заголовочного файла директиву препроцессора #pragma со значением once , которая также препятствует повторному включению файла (в старых компиляторах директива может не поддерживаться):

Название заголовочного файла в директиве #include может быть указано внутри угловых скобок:

или внутри кавычек:

В первом случае заголовочный файл ищется в путях поиска заголовочных файлов. При этом текущий рабочий каталог не просматривается. Добавить каталог в пути поиска заголовочных файлов позволяет флаг -I в команде компиляции. Обычно с помощью угловых скобок включаются заголовочные файлы стандартной библиотеки или библиотеки стороннего разработчика.

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

Внимательный читатель наверняка обратил внимание на то, что файл iostream не содержит расширение. Наличие расширения файла принято в стандартной библиотеке языка C. В стандартной библиотеке языка C++ расширение файла принято не указывать. Так как язык C++ наследует все библиотеки языка C, то файлы можно подключать как в стиле языка C, так и в стиле языка C++. Например, файл string.h из стандартной библиотеки языка C доступен в языке C++ под названием cstring , а файл math.h под названием cmath . Отличие между этими способами подключения заключается в импорте идентификаторов. В языке C при подключении файла (например, math.h ) все идентификаторы импортируются в глобальное пространство имен, а в языке C++ при подключении файла (например, cmath ) идентификаторы добавляются в пространство имен под названием std . Поэтому перед идентификатором необходимо указать название пространства имен (например, std::cout ). Использование пространств имен позволяет избежать конфликта имен в программе.

Можно указать просто название заголовочного файла:

абсолютный путь к нему:

или относительный путь к нему:

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

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

Учебник C++ (Qt Creator и MinGW)

Учебник C++ (Qt Creator и MinGW) в формате PDF

Помощь сайту

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов

Горин Павел/ автор статьи

Павел Горин — психолог и автор популярных статей о внутреннем мире человека. Он работает с темами самооценки, отношений и личного роста. Его экспертность основана на практическом консультировании и современных психологических подходах.

Понравилась статья? Поделиться с друзьями:
psihologiya-otnosheniy.ru
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: