Файлы заголовков (.h, .hpp, .hh, .hxx)
#include не подключает никаких библиотек!
#include
#include "mymodule.h"
Файл stdio.h - это вполне реальный файл, скорее всего находится он в /usr/include.
Важно понимать, что в этом файле содержится только заголовок функции printf, а самой функции там нет. Отсюда используемый суффикс “.h” - header. Всё что узнает компилятор видя заголовок - это что где-то есть функция с таким-то именем, принимающая столько-то параметров таких-то типов, что делает функция компилятору знать не нужно. Дело в том, что компилятор генерирует не готовый машинный код, а объектный файл, в котором пока что не хватает некоторых адресов. В данном случае результатом работы компилятора становится модуль, не содержащий самой функции printf. Вместо неё модуль содержит указание на то, что линкеру следует откуда-то добыть функцию с таким именем, а её адрес подставить куда следует. То есть функция print появится в программе только на этапе окончательной сборки. (более того в реальной ситуации, скорее всего, функция вообще не будет содержатся в исполняемом файле программы, после её запуска функция будет подгружена из динамической билиотеки).
!!!ОЧЕНЬ ЧАСТАЯ ОШИБКА: На вопрос что делает директива #include многие отвечают что она “подключает библиотеку”, а сам файл stdio.h - это якобы библиотека. Так вот, директива #include не имеет никакого отношения к подключению библиотек, а заголовочные файлы библиотеками не являются - они являются заголовочными файлами и ничем иным. Яп Си вообще не предусматривает возможность подключить библиотеку из текста программы, компилятор этим не занимается.
А теперь к сути дела
Допустим что наша программа разбита на 2 файла: main.cpp и func.cpp
// func.cpp
#include <iostream>
void func()
{
std::cout << "Hello" << endl;
}
// main.cpp
int main()
{
func();
return 0;
}
И мы хотим использовать функцию func(), которая определена в func.cpp из main.cpp. Просто так её вызвать у нас не выйдёт, поскольку main.cpp понятия не имеет что она делает. Для того что бы её вызвать, нам необходимо объявить её перед функцией main: указать тип возврата и параметры, которые она принимает. Таким образом мы как будто обещаем компилятору, что где-то там, попозже мы определим эту функцию.
// main.cpp
void func();
int main()
{
func();
return 0;
}
А теперь представим такую ситуацию: функция func.cpp используеться не только в main.cpp, а и в нескольки других файлах. Всё бы ничего, до тех пор пока мы не захотим изменить “начинку” функции func(), например поменять ей тип возврата либо параметры. В таком случае нам понадобиться изменить объявление этой функции во всех файлах, которые её используют. Что бы не мучаться мы создадим ещё один файл - заголовков, в котором будет лежать как раз таки объявление функции func().
// header.h
void func();
Подключим header.h к main.cpp вместо объявления функции:
// main.cpp
#include "./header.h"
int main()
{
func();
return 0;
}
Теперь при компиляции строка #include “./header.h” заменится на содержимое файла заголовков header.h, теперь для изменения функции func() потребуется только два редактирования: func.cpp и header.h.
Двойное включение заголовочного файла
Может случится такая ситуация:
// main.cpp
#include "./func.h"
#include "./header.h"
...
// func.h
#include "./header.h"
...
Мы подключаем к main.cpp два файла заголовков: func.h и header.h, НО func.h подключает еще раз header.h. Зная что директива #include при компиляции будет заменена на содержимое файла, то выходит такая ситуация:
// main.cpp
#include "./header.h"
#include "./header.h"
...
Есть несколько способов избежать двойного включения (стражи включения):
Заголовочный файл:
Использование макросов
#ifndef FUNC_H
#define FUNC_H
int func();
#endif
Более простой
#pragma once
int func();
Аналогично объявлению функции (когда пишем только заголовок функции без тела) можно объявить переменную без определения:
extern int a; // объявить
int a; // объявить и определить (выделить под неё память)