Директива #include. Включение файлов


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

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

Мы можем использовать следующие директивы:

#define: определяет макрос или препроцессорный идентификатор

#undef: отменяет определение макроса или идентификатора

#ifdef: проверяет, определен ли идентификатор

#ifndef: проверяет неопределенности идентификатор

#include: включает текст из файла

#if: проверяет условие выражение (как условная конструкция if)

#else: задает альтернативное условие для #if

#endif: окончание условной директивы #if

#elif: задает альтернативное условие для #if

#line: меняет номер следующей ниже строки

#error: формирует текст сообщения об ошибке трансляции

#pragma: определяет действия, которые зависят от конкретной реализации компилятора

#: пустая директива, по сути ничего не делает

Рассмотрим основные из этих директив.

Директива #include. Включение файлов
Ранее уже использовалась директива #include. Эта директива подключает в исходный текст файлы. Она имеет следующие формы применения:

1
2
#include <имя_файла> // имя файла в угловых скобках
#include "имя_файла" // имя файла в кавычках
Например, если нам надо задействовать в приложении консольный ввод-вывод с помощью функций printf() или scanf(), то нам надо подключить файл "stdio.h", который содержит определение этих функций:

1
#include <stdio.h>
При выполнении этой директивы препроцессор вставляет текст файла stdio.h. Данный файл еще называется заголовочным. Заголовочные файлы содержат прототипы функций, определения и описания типов и констант и имеют расширение .h.

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

assert.h: отвечает за диагностику программ

complex.h: для работы с комплексными числами

ctype.h: отвечает за преобразование и проверку символов

errno.h: отвечает за проверку ошибок

fenv.h: для доступа к окружению, которое управляет операциями с числами с плавающей точкой

float.h: отвечает за работу с числами с плавающей точкой

inttypes.h: для работы с большими целыми числами

iso646.h: содержит ряд определений, которые расширяют ряд логических операций

limits.h: содержит предельные значения целочисленных типов

locale.h: отвечает за работу с локальной культурой

math.h: для работы с математическими выражениями

setjmp.h: определяет возможности нелокальных переходов

signal.h: для обработки исключительных ситуаций

stdalign.h: для выравнивания типов

stdarg.h: обеспечивает поддержку переменного числа параметров

stdatomic.h: для выполнения атомарных операций по разделяемым данным между потоками

stdbool.h: для работы с типом _Bool

stddef.h: содержит ряд вспомогательных определений

stdint.h: для работы с целыми числами

stdio.h: для работы со средствами ввода-вывода

stdlib.h: содержит определения и прототипы функций общего пользования

stdnoreturn.h: содержит макрос noreturn

string.h: для работы со строками

tgmath.h: подключает math.h и complex.h плюс добавляет дополнительные возможности по работе с математическими вычислениями

threads.h: для работы с потоками

time.h: для работы с датами и временем

uchar.h: для работы с символами в кодировке Unicode

wchar.h: для работы с символами

wctype.h: содержит дополнительные возможности для работы с символами

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

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

1
int number = 5;
Здесь просто определена одна переменная. Теперь подключим этот файл в программу:

1
2
3
4
5
6
7
8
#include <stdio.h>
#include "main.c"

int main(void)
{
printf("%d", number); // 5
return 0;
}
При подключении своих файлов их имя указывается в кавычках. И несмотря на то, что в программе не определена переменная number, она будет браться из подключенного файла main.c. Но опять же отмечу, важно, что в данном случае файл main.c располагается в одной папке с главным файлов программы.

В то же время данный способ прекрасно работает в GCC. Но для разных сред программирования способ подключения файлов может отличаться. Например, в Visual Studio мы получим ошибку. И более правильный подход будет состоять в том, что определить объявление объекта (переменной/константы) или функции в дополнительном заголовочном файле, а определение объекта или функции поместить в стандартный файл с расширением .c.

Например, в нашем в файле main.c уже есть определение переменной number. Теперь в ту же папку добавим новый файл main.h - файл с тем же названием, но другим расширением. И определим в main.h следующий код:

1
extern int number;
Ключевое слово extern указывает, что данный объект является внешним. И в этом случае мы могули бы его подключить в файл исходного кода:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include "main.h" // объявление или описание объекта
#include "main.c" // определение объекта

int main(void)
{
printf("%d", number);
return 0;
}
Этот пример также будет работать в GCC, однако как уже было рассмотрено выше, подключение файла main.h для GCC необязательно.

Если разработка ведется в Visual Studio, то определение объекта не надо подключать исходный файл:

1
2
3
4
5
6
7
8
#include <stdio.h>
#include "main.h" // объявление или описание объекта

int main(void)
{
printf("%d", number);
return 0;
}
Несмотря на то, что здесь явным образом не подключается файл main.c, но при трансляции Visual Studio через заголовочный файл main.h сможет подключить находящийся в той же папке файл main.c.

include directive in Visual Studio