Есть ли стандартное требование для поиска C++ #include?
Недавно я узнал, что gnu cpp и msvc cl лечат #include
файлы по разному. Обычным поведением является поиск в каталоге рядом с файлом included_ing_, а затем итерация по пути включения (который, очевидно, устанавливается с помощью -I
или же /I
аргументы компилятора.)
Но теперь я задаюсь вопросом: правильно ли я учился или на самом деле есть стандарт для препроцессора?
3 ответа
Согласно стандарту, оба #include <...>
а также #include "..."
должен искать реализацию определенного набора мест. Нет даже требования, чтобы имя, указанное между <>
или же ""
это имя файла, просто оно позволяет уникально идентифицировать заголовок.
Сказав, что если реализация хочет иметь какую-либо надежду на компиляцию существующего источника, ей лучше следовать тем же соглашениям, что и остальным: <...>
относится к системному включаемому файлу, и "..."
относится к вашим собственным включаемым файлам (и каталог, содержащий исходный файл, следует искать в первую очередь). Ни одно из этих требований не является стандартным, но без них у вас нет возможности скомпилировать практически любой существующий код.
Стандарт немного шизофреничен - в одном месте он даст вам инструменты для работы с файлами и каталогами (расширение файловой системы в C++17), в то время как в других он отказывается признать, что такие вещи, как файлы и каталоги, даже существуют, Предположительно, это связано с тем, что авторы не хотят слишком много возиться с древним текстом, поскольку сейчас он работает нормально и может просто случайно что-то сломать, но выглядит немного забавно.
Чтобы расширить ответ Г. Гуджита, ни стандарты языка C или C++ не дают здесь никаких гарантий, но стандарт POSIX действительно предоставляет некоторые дополнительные гарантии того, как cpp
(C P repcessor) должен функционировать. Например, в описании-I
опция командной строки:
Таким образом, заголовки, имена которых заключены в двойные кавычки (
""
) должен быть найден первым в каталоге файла с#include
линия, затем в каталогах, названных в-I
варианты, и последний в обычных местах. Для заголовков, имена которых заключены в угловые скобки ("<>
") заголовок нужно искать только в каталогах, названных в-I
варианты и то в обычных местах.
Согласно стандарту (16.2 [cpp.include]/2), #include <...>
ищет последовательность мест, определенных реализацией, для заголовка, уникально идентифицированного указанной последовательностью между
<
а также>
разделители. Как места определяются или как определяется заголовок, определяется реализацией. [выделение добавлено]
В отличие от этого (16,2 [cpp.include]/3), #include "..."
вызывает замену этой директивы всем содержимым исходного файла, идентифицируемого указанной последовательностью между
"
разделители. Именованный исходный файл ищется в соответствии с реализацией. Если этот поиск не поддерживается или поиск не удался, директива обрабатывается так, как если бы она читалась#include <...>
с идентичной содержащейся последовательностью (включая>
символы, если таковые имеются) из оригинальной директивы. [выделение добавлено]
Таким образом, места и методы полностью определяются реализацией, за исключением того, что #include "..."
отступает на #include <...>
если исходный файл не найден в начальном поиске.
Важна разница в формулировке: #include <...>
не требует исходного файла; полностью зависит от реализации, как он обрабатывает стандартные заголовки. Пользовательские заголовочные файлы должны быть извлечены только с #include "..."
, поскольку это тот, который требуется для обработки исходных файлов.
По большей части компиляторы реализуют эти вещи так, как вы ожидаете. Критическое различие, которое вы видите с MSVC, заключается в том, что для #include "..."
сначала он ищет в каталоге исходного файла верхнего уровня, который компилируется, в то время как другие компиляторы сначала ищут в каталоге исходного файла, который компилируется. Это имеет значение, когда заголовочный файл имеет #include "..."
директива: большинство компиляторов ищут в каталоге, где находится заголовочный файл, но MSVC ищет, как если бы #include "..."
Директива была в исходном файле верхнего уровня. Оба действительны, и есть аргументы для обоих подходов, хотя я лично нахожу поиск в каталоге, где заголовок, более интуитивным и с ним легче работать.