Как компилятор узнает из заголовочного файла, что исходный файл где-то существует?

Я только начал изучать C/C++, но немного запутался. Я часто вижу директиву препроцессора #include с аргументом файла заголовка:

#include <stdio.h>
#include <iostream.h> 
#include "windows.h"
#include <math.h>

Но иногда .h отсутствует:

#include <iostream>

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

Если я создам библиотеку, то куда положить файлы .c/.cpp/.h и как их подключить?


person Iter Ator    schedule 18.11.2015    source источник
comment
Хороший пример того, почему не стоит смешивать C и C++.   -  person Sourav Ghosh    schedule 18.11.2015
comment
C/C++ - это не язык. Какой из этих двух языков вы изучаете и о каком языке ваш вопрос?   -  person fuz    schedule 18.11.2015
comment
(1) Не стремитесь выучить несуществующий язык C/C++. Выбери один. Я бы рекомендовал С++. (2) Разместите настоящий код. Похоже, @wilx исправил ваш код. Это была услуга для читателей, но плохая услуга для вас, потому что вам действительно нужно научиться публиковать настоящий код, чтобы другим не пришлось что-то исправлять за вас или вводить в заблуждение ошибками в опубликованном вами коде.   -  person Cheers and hth. - Alf    schedule 18.11.2015


Ответы (4)


При установке компилятора стандартные файлы заголовков и библиотеки также устанавливаются в известные места. Например, в системе Linux или Unix стандартные заголовки обычно устанавливаются под /usr/include или /usr/local/include, а стандартные библиотеки обычно устанавливаются под /usr/lib или /usr/local/lib.

Когда вы #include заголовочный файл, компилятор будет искать его в разных местах в зависимости от того, используете ли вы угловые скобки (#include <stdio.h>) или кавычки (#include "myfile.h"). Например, если вы включите файл заголовка в угловые скобки, gcc будет искать этот заголовок в следующих каталогах:

/usr/local/include
libdir/gcc/target/version/include
/usr/target/include
/usr/include

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

Большинство компиляторов позволяют указать дополнительные пути включения в качестве аргументов для команды компиляции, например

gcc -o executable-name -I /additional/include/path source-files

Но иногда .h отсутствует:

#include <iostream>

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

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

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

gcc -o executable-name command-line-options source-files -lm

При использовании gcc соглашение заключается в том, что -lname ссылается на библиотеку с именем libname.a2. Стандартный файл математической библиотеки называется libm.a, поэтому -lm сообщает gcc, что следует связать машинный код, содержащийся в libm.a.

Как и стандартные заголовки, компилятор будет искать стандартные библиотеки в известных местах. Вы можете указать дополнительные пути поиска для библиотек, которые не входят в стандартную установку, например:

gcc -o executable-name command-line-options source-files -L /additional/library/path -ladditional-library

Если я создам библиотеку, то куда положить файлы .c/.cpp/.h и как их подключить?

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

mkdir ~/mylib
cd ~/mylib

Затем вы создаете и редактируете свои исходные файлы и заголовки:

cd ~/mylib
vi mylib.h
vi mylib.c

Чтобы собрать mylib как статическую библиотеку, сделайте следующее:

cd ~/mylib
gcc -c mylib.c
ar libmylib.a mylib.o

Итак, когда вы закончите, каталог ~/mylib будет содержать следующее:

libmylib.a
mylib.c 
mylib.h
mylib.o

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

gcc -o executable-name command-line-options source-files -I ~/mylib -L ~/mylib -lmylib

Таким образом, если вы include "mylib.h" в другой программе, это скажет компилятору искать этот заголовок в ~/mylib, и он сообщит компоновщику, что библиотека libmylib.a будет найдена в ~/mylib.

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

gcc -o executable-name command-line-options source-files -I ~/mylib/include -L ~/mylib/lib -lmylib


1. Так было не всегда; самые ранние реализации C++ использовали iostream.h, vector.h и т. д. Я не уверен, когда именно это соглашение изменилось, но это было давно.

2. На самом деле, я больше не уверен, является ли это gcc конвенция или *nix конвенция; эти двое были соединены на бедре так долго, что иногда трудно вспомнить.

person John Bode    schedule 18.11.2015

На самом деле компилятор ничего не знает о других исходных файлах, он знает только о текущем < em>единица перевода. Работа компоновщика состоит в том, чтобы выполнять все переводы. модули и связать их вместе с библиотеками для создания исполняемой программы, и именно компоновщик разрешит все определения и заметит отсутствующие определения функций.

Традиционно работа с C или C++ представляет собой четырехэтапный процесс:

  1. Редактировать файлы
  2. Компилировать исходные файлы в объектные файлы
  3. Связывание объектных файлов и библиотек для создания исполняемой программы
  4. Запустите программу.

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


Что касается «отсутствующего» .h, ни один из заголовочных файлов стандартной библиотеки только для C++ не имеет такого расширения.

person Some programmer dude    schedule 18.11.2015

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

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

Компилятор имеет расположение по умолчанию для заголовков, используя ‹ > и используя " ". Заголовки, использующие " ", могут включать относительные или абсолютные пути. Относительный путь может варьироваться в зависимости от набора инструментов.

person rcgldr    schedule 18.11.2015

Re

Как компилятор узнает из заголовочного файла, что исходный файл где-то существует?

Как правило, то, как компилятор находит файлы заголовков и являются ли они файлами, зависит от компилятора.

Но обычно используется один или несколько из следующих механизмов:

  • Компилятор знает местоположение своей программы и может искать стандартные заголовки в включаемых каталогах относительно этого местоположения. g++ делает это.

  • Компилятор может быть настроен с путями для включения каталогов в одну или несколько переменных среды. Например. g++ использует CPATH (среди прочего), а Visual C++ использует INCLUDE (и, возможно, больше).

  • Компилятор может принимать включаемые пути к каталогам в качестве параметров командной строки. Обычно это -I или с Visual C++ альтернативно /I.

  • Компилятор можно настроить с помощью файла конфигурации в известном месте. Например, g++ использует файл specs.

  • Компилятор может принимать один или несколько файлов конфигурации в качестве параметров командной строки. Например. Visual C++ принимает так называемые "файлы ответов", а g++ принимает файлы спецификаций, хотя и с несколько иной семантикой, чем файл спецификаций общей конфигурации.

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


Re

Но когда и как компилятор включает математические функции, если я включаю math.h?

Это не так.

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

Когда вы используете стороннююrd библиотеку, вам, как правило, придется явно связываться с этой библиотекой, помимо включения одного или нескольких ее заголовков. Компилятор Visual C++ поддерживает директивы #pragma, которые могут быть помещены в заголовки и служат для автоматического управления компоновщиком. Но это не стандартизировано, и, например. g++ не поддерживает этот автоматический механизм.


Re

Если я создам библиотеку, то куда поместить файлы .c/.cpp/.h и как их подключить?

По сути, у вас есть полная свобода действий, вы можете делать это так, как хотите. Это проблематично, потому что пользователям библиотеки C++ приходится иметь дело с разными соглашениями и инструментами для каждой библиотеки, вплоть до использования разных ОС для «настройки» вещей. Однако в настоящее время популярно использовать CMake для библиотек с отдельной компиляцией, что помогает уменьшить количество проблем, связанных с самостоятельным созданием.

Хорошей альтернативой является создание библиотеки только заголовков со всем кодом в заголовках. Большие части Boost — это только заголовки. Преимущество заключается в том, что он очень прост в использовании, не требует конфигурации или адаптации набора инструментов или чего-то еще, но с популярной в настоящее время технологией сборки 1950-х годов это может привести к увеличению времени сборки.

person Cheers and hth. - Alf    schedule 18.11.2015