Почему в проектах используется переключатель -I с учетом опасностей?
Читая мелкий шрифт переключателя -I
в GCC, я довольно шокирован, обнаружив, что использование его в командной строке переопределяет систему. Из документов препроцессора
"Вы можете использовать -I
для переопределения файла системного заголовка, заменив свою собственную версию, так как эти каталоги ищутся перед стандартными каталогами файлов системного заголовка".
Кажется, они не лгут. В двух разных системах Ubuntu с GCC 7, если я создаю файл endian.h
:
#error "This endian.h shouldn't be included"
... а затем в том же каталоге создайте main.cpp
(или main.c, с той же разницей):
#include <stdlib.h>
int main() {}
Затем компилируем с g++ main.cpp -I. -o main
g++ main.cpp -I. -o main
(или лязг, g++ main.cpp -I. -o main
же разница) дает мне:
In file included from /usr/include/x86_64-linux-gnu/sys/types.h:194:0,
from /usr/include/stdlib.h:394,
from /usr/include/c++/7/cstdlib:75,
from /usr/include/c++/7/stdlib.h:36,
from main.cpp:1:
./endian.h:1:2: error: #error "This endian.h shouldn't be included"
Таким образом, stdlib.h включает этот файл types.h, который в строке 194 просто говорит #include <endian.h>
. Мое очевидное заблуждение (и, возможно, чужое) заключалось в том, что угловые скобки предотвратили бы это, но -I сильнее, чем я думал.
Хотя он недостаточно силен, потому что вы даже не можете исправить это, сначала введя /usr/include в командной строке, потому что:
"Если стандартный системный каталог включения или каталог, указанный с -Isystem
, также указан с -I
, опция -I
игнорируется. Каталог по-прежнему ищется, но как системный каталог на своей обычной позиции в Система включает в себя ".
Действительно, подробный вывод для g++ -v main.cpp -I/usr/include -I. -o main
g++ -v main.cpp -I/usr/include -I. -o main
листья /usr/включают внизу списка:
#include "..." search starts here:
#include <...> search starts here:
.
/usr/include/c++/7
/usr/include/x86_64-linux-gnu/c++/7
/usr/include/c++/7/backward
/usr/lib/gcc/x86_64-linux-gnu/7/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
Цвет меня удивил. Я думаю, чтобы сделать это вопрос:
Какая законная причина для большинства проектов использовать -I
учитывая эту чрезвычайно серьезную проблему? Вы можете переопределить произвольные заголовки в системах на основе случайных конфликтов имен. -Iquote
не все должны вместо этого использовать -Iquote
?
Ответы
Ответ 1
Какие есть законные причины для -I
сверх -Iquote
? -I
стандартизирован (по крайней мере, POSIX), а -Iquote
нет. (Практически я использую -I
потому что tinycc (один из компиляторов, с которыми я хочу компилировать мой проект) не поддерживает -Iquote
.)
Как проекты справляются с -I
учетом опасностей? У вас есть включенные файлы в каталоге, и вы используете -I, чтобы добавить каталог, содержащий этот каталог.
- файловая система:
includes/mylib/endian.h
- командная строка:
-Iincludes
- Файл C/C++:
#include "mylib/endian.h"//or <mylib/endian.h>
При этом, пока вы не конфликтуете с именем mylib
, вы не конфликтуете (по крайней мере, в отношении имен заголовков).
Ответ 2
Оглядываясь назад на руководства GCC, похоже, что -iquote
и другие параметры были добавлены только в GCC 4: https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Directory-Options.html#Directory %20Options
Таким образом, использование "-I"
, вероятно, представляет собой некоторую комбинацию: привычки, ленивости, обратной совместимости, незнания новых опций, совместимости с другими компиляторами.
Решением является "пространство имен" ваших заголовочных файлов, помещая их в подкаталоги. Например, поместите свой заголовок endian в "include/mylib/endian.h"
затем добавьте "-Iinclude"
в командную строку, и вы можете #include "mylib/endian.h"
который не должен конфликтовать с другими библиотеками или системой библиотеки.
Ответ 3
Я -I
это опасно, что -I
ложно. Язык оставляет поиск файлов заголовков с любой формой #include
достаточной степени определенной реализацией, так что небезопасно использовать файлы заголовков, которые вообще конфликтуют с именами стандартных файлов заголовков. Просто воздержитесь от этого.
Ответ 4
Очевидный случай - кросс-компиляция. GCC немного страдает от исторического предположения UNIX, что вы всегда компилируете для своей локальной системы или, по крайней мере, чего-то очень близкого. Поэтому заголовочные файлы компилятора находятся в корне системы. Чистый интерфейс отсутствует.
Для сравнения, Windows не предполагает компилятор, а компиляторы Windows не предполагают, что вы нацелены на локальную систему. Поэтому вы можете установить набор компиляторов и набор SDK.
Теперь в кросс-компиляции GCC ведет себя намного больше как компилятор для Windows. Он больше не предполагает, что вы намереваетесь использовать заголовки локальной системы, но позволяет вам точно указать, какие заголовки вы хотите. И, очевидно, то же самое относится и к библиотекам, на которые вы ссылаетесь.
Теперь обратите внимание, что когда вы делаете это, набор сменных заголовков предназначен для того, чтобы идти поверх базовой системы. Вы можете не указывать заголовки в наборе замен, если их реализация будет идентичной. Например, есть вероятность, что <complex.h>
то же самое. Там не так много изменений в реализации комплексных чисел. Однако вы не можете случайно заменить внутренние биты реализации, такие как <endian.h>
.
TL, DR: этот вариант, если для людей, которые знают, что они делают. "Быть небезопасным" не является аргументом для целевой аудитории.