Где gcc ищет заголовочные файлы

Обновлено: 07.07.2024

GCC - GNU compiler collection – это инструментальное средство разработки программ на языках Си, Си++, Фортран и других.

В состав GCC входят:

  • Препроцессоры программ на языках Си и Си++.
  • Компиляторы для поддерживаемых языков. В мире Unix под компилятором (в узком смысле) понимается программа, выдающая в качестве результата текст программы на языке ассемблера.
  • Стандартные библиотеки языков Си++ и других (кроме Си).
  • Программы-драйверы компиляции, которые предоставляют универсальный интерфейс командной строки ко всем компонентам GCC и связанным с ними системным утилитам. Например, программа gcc позволяет управлять компиляцией программ на Си, g++ - компиляцией программ на Си++ и т. д.

В состав GCC не входят:

  • Ассемблер (GNU Assembler, команда as), компоновщик (GNU linker, команда ld1 ) и некоторые другие утилиты для работы с объектными и исполняемыми файлами. В Linux они находятся в инсталляционном пакете binutils.
  • Заголовочные файлы и объектные модули стандартной библиотеки языка Си. В Linux они находятся в инсталляционных пакетах glibc, glibc-devel, glibc-static

Тем не менее, они необходимы для компиляции программ на Си, ввиду чего будут рассмотрены наряду с инструментами GCC. Команда запуска GCC для языка Си в общем виде выглядит следующим образом:

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

Схема трансляции программ написанных на Си

Трансляция программы состоит из следующих этапов:

  • препроцессирование;
  • трансляция в ассемблер;
  • ассемблирование;
  • компоновка.

Традиционно исходные файлы программы на языке Си имеют суффикс имени файла .c, заголовочные файлы для программы на Си имеют суффикс .h. В файловых системах Unix регистр букв значим, и если, например, имя файла имеет суффикс .C, такой файл считается содержащим текст программы на языке Си++, и будет компилироваться компилятором языка Си++, а не Си

Препроцессирование.

Трансляция в ассемблер.

На вход подаётся одна единица трансляции, а на выходе (при отсутствии синтаксических и семантических ошибок) выдаётся файл на языке ассемблера для (как правило) машины, на которой ведётся трансляция. Файл с оттранслированной программой на языке ассемблера имеет суффикс имени .s, но точно так же, как и результат работы препроцессора, он по умолчанию удаляется.

Ассемблирование.

На этой стадии работает ассемблер. Он получает на входе результат работы предыдущей стадии и генерирует на выходе объектный файл. Объектные файлы в UNIX имеют суффикс .o

Компоновка.

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

Запуск транслятора gcc

Рассмотрим основные возможности транслятора GNU Си. В командной строке задаётся список файлов для обработки. Какие операции необходимо выполнить с файлами – зависит от суффикса имен файлов. Возможные суффиксы перечислены в таблице ниже. Если имя файла имеет нераспознанный суффикс, это имя передаётся компоновщику

Действия по трансляции файла определяются для каждого указанного в командной строке файла индивидуально. Например, если в командной строке указаны имена файлов 1.c и 2.o, то для первого файла будут выполнены все шаги трансляции, а для второго – только компоновка. Исполняемый файл будет содержать результат трансляции первого файла, скомпонованный со вторым файлом и стандартными библиотеками.

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

Например, командная строка

транслирует два файла на языке Си, объединяя их в одну программу с именем 1.

компонует два объектных файла, добавляя к ним стандартную библиотеку языка Си и стандартную математическую библиотеку (опция -lm), и помещает результат в исполняемый файл с именем 3. Прочие полезные опции транслятора gcc перечислены в таблице.

Использование стандартных библиотек языка Си

В языках Си и Си++ библиотеки состоят из двух частей:

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

Заголовочные файлы стандартной библиотеки находятся в каталоге /usr/include и его подкаталогах, например, /usr/include/stdio.h или /usr/include/sys/types.h. Программа-драйвер gcc автоматически добавляет этот каталог в список для поиска заголовочных файлов, поэтому каталог /usr/include не нужно задавать в опции –I.

Файлы динамических библиотек размещаются в каталоге /lib или /usr/lib, а файлы статических библиотек – в каталоге /usr/lib. Они задаются автоматически и опция –L для них не нужна. Файл динамической библиотеки языка Си называется libc.so и полный путь к нему – /lib/libc.so.

Таким образом, если выписать явно пути и библиотеки, задаваемые при компиляции программы на Си с помощью gcc неявно, мы получим примерно следующую командную строку:

Исключением являются математические функции стандартной библиотеки Си, объявленные в заголовочном файле <math.h>, например, sin. Их реализации вынесены в отдельную библиотеку libm.so (libm.a), которая не указывается в списке подключаемых библиотек по умолчанию. Для компоновки программ, использующих математические функции, необходимо в командной строке gcc указать опцию -lm:

Для того, чтобы увидеть все пути, передаваемые драйвером компиляции gcc препроцессору, компилятору, ассемблеру и компоновщику, можно использовать опцию -v:

Компоновка программы

Если исполняемая программа компонуется из нескольких единиц трансляции, компоновщик использует свои правила видимости имён, которые приведены ниже:

Последнее правило можно продемонстрировать на следующем примере. Предположим, что в трёх файлах определена переменная var следующим образом:

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

Программы из нескольких единиц трансляции

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

При группировке функций и переменных по исходным файлам логически сильно связанные функции объединяются в один исходный файл. Данная рекомендация соответствует способу реализации на Си парадигмы модульного программирования, предполагающего разбиение программы на независимые части – модули. Например, если речь идет о программе-редакторе табличных данных, функции обеспечивающие сохранение таблицы в файл, могут быть помещены в один исходный файл, функции, которые выводят на экран содержимое таблицы, – в другой файл, функции, которые анализируют ввод пользователя, – в третий.

Чем больше переменных объявлено в единице компиляции с классом памяти static вместо класса памяти по умолчанию, тем лучше. Программа может быть легче модифицирована, если доступ к данным всегда происходит с помощью вызовов функций. Чем меньше "чужих" переменных использует некоторая единица компиляции, тем она проще для понимания.

Для каждого .c файла должен существовать интерфейсный файл с тем же именем, но суффиксом .h, в котором определяются переменные, функции, типы данных и пр., которые могут использоваться извне данной единицы компиляции.

Исходный .c файл должен обязательно подключать свой собственный .h файл. В этом случае транслятор обнаружит рассогласования между объявлениями в .h-файлах и определениями в .c файле.

Интерфейсный .h файл должен быть обязательно защищён от повторного включения т.н. стражем включения (англ., include guard):

Здесь NAME – это имя файла (без суффикса). Некоторые .h- файлы могут включать другие .h файлы, поэтому, когда программа становится большой, человек не может отследить, какие файлы уже включались, а какие – ещё нет. При использовании стражей включения можно не задумываясь включать в .c файл все заголовочные файлы, необходимые данной единице компиляции. Защита от повторного включения предотвращает появление ошибок о переопределённых типах, переменных и функциях.

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

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

в системе Unix, где gcc ищет файлы заголовков?

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

эта команда спрашивает gcc, который C++ препроцессор, который он использует, а затем спрашивает, что препроцессор, где он ищет, включает.

вы получите надежный ответ для вашей специфической установки.

кроме того, для C препроцессора:

кроме того, gcc будет искать в каталогах, указанных после .

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

на раздел CPP на руководство GCC указывает, что заголовочные файлы могут находиться в следующих каталогах:

для программ на C++ он также будет выглядеть в /usr / include/g++ - v3, первый.

чтобы заставить GCC распечатать полный набор каталогов, где он будет искать системные заголовки, вызовите его следующим образом:

который произведет выход формы

если у вас -I -семейные параметры в командной строке, они будут влиять на то, что распечатывается.

(том - избавиться от всех другое мусор этот вызов печатает, и LC_ALL=C обеспечить работает - "начинается здесь "и" конец списка поиска " фразы are переведено IIRC.)

набор путей, по которым компилятор ищет файлы заголовков, можно проверить командой: -

можно просмотреть (дополнительный) путь включения для программы C из bash, проверив следующее:

Если это пусто, его можно изменить, чтобы добавить местоположения по умолчанию include, по:

Это каталоги, которые gcc ищет по умолчанию для указанных файлов заголовков (учитывая, что файлы заголовков включены в шевроны ); 1. / usr / local/ include / --используется для файлов заголовков сторонних производителей. 2. /usr / include / -- используется для системных заголовочных файлов.

Путь поиска для заголовочных файлов и библиотек Linux

1.1 В какую папку устанавливаются библиотечные файлы .so и заголовочные файлы .h при установке glibc, как gcc может правильно найти соответствующие папки?

Когда мы устанавливаем –prefix = / path / при сборке gcc, gcc устанавливается в / path /. По умолчанию gcc будет искать библиотеки в / path / lib / и искать файлы заголовков в / path / include /.
Например, в обычных системах Linux и gcc, и glibc установлены в каталоге / usr /, поэтому исполняемый файл gcc находится в каталоге / usr / bin, а файл библиотеки glibc находится в / usr / lib В этом каталоге файл заголовка glibc находится в каталоге / usr / include.
Разумеется, указанное выше соотношение не является точным и может быть указано вручную при установке gcc и glibc. Вы можете использовать команду "gcc -print-search-dirs" для просмотра пути поиска библиотеки gcc:

1

Вы можете использовать следующие методы, чтобы изменить путь к библиотеке и файлу заголовка, который gcc ищет по умолчанию:

  • (1) Используйте «gcc -nostdlib -Lusrlibpath», чтобы изменить путь поиска библиотеки по умолчанию для gcc, и используйте «gcc -nostdinc -Iusrincpath», чтобы изменить путь поиска заголовка по умолчанию для gcc.
  • (2) Используйте «gcc -Wl, -sysroot =», чтобы изменить путь поиска библиотеки по умолчанию для gcc.
  • (3) Измените файл спецификации gcc, чтобы настроить любые параметры gcc.

Спецификации - это файлы конфигурации gcc. Этот метод модификации является самым мощным, но он также является самым сложным и рискованным, поскольку файл спецификации gcc неясен и труден для понимания. Пока я не буду изменять его, но сначала я расскажу о существовании этого метода.
Используйте команду "gcc -v", чтобы просмотреть путь к спецификациям gcc:

2

В некоторых установках gcc используются встроенные спецификации, но нет внешних файлов спецификаций:

3

Для встроенной спецификации вы можете использовать команду "gcc -dumpspecs" для просмотра содержимого конкретной спецификации:

4

Вы также можете использовать "gcc -specs =", чтобы указать файл спецификации gcc.

  • (4) Передается ли конфигурация gcc по умолчанию командой configure во время сборки?

Используйте команду "gcc -v" для просмотра значения конфигурации gcc. Параметры конфигурации описаны в файле gcc-4.1.0 \ gcc \ doc \ install.texi.

5

1.2 Каковы пути поиска библиотеки ссылок, когда программа связана?

При связывании конфигурация пути поиска библиотеки имеет следующие методы: (Для конкретного использования, пожалуйста, обратитесь к описанию в главе примера)

  • (1) Используйте «gcc -Llibpath -llibname», чтобы указать путь поиска библиотеки ссылок.
  • (2) Используйте переменную среды "LIBRARY_PATH", чтобы указать путь поиска библиотеки ссылок.
  • (3) Используйте «SEARCH_DIR» в сценарии подключения ld, чтобы указать путь поиска библиотеки ссылок.

1.3 Когда программа работает, каковы пути поиска в библиотеке динамических ссылок?

При связывании конфигурация пути поиска библиотеки имеет следующие методы: (Для конкретного использования, пожалуйста, обратитесь к описанию в главе примера)

  • (1) При компиляции используйте «gcc -Wl, -rpath», чтобы указать путь поиска библиотеки динамических ссылок во время выполнения, и жестко запишите его в опцию «RPATH» файла ELF.
  • (2) Переменная среды «LD_RUN_PATH» используется для указания пути поиска библиотеки динамических ссылок во время компиляции, и она жестко задана в опции «RPATH» файла ELF.
  • (3) Используйте переменную среды "LD_LIBRARY_PATH", чтобы указать путь поиска библиотеки динамических ссылок во время выполнения.
  • (4) Используйте файл конфигурации "/etc/ld.so.conf", чтобы указать путь поиска библиотеки динамических ссылок во время выполнения.
  • (5) Используйте путь поиска по умолчанию "/ lib, / usr / lib" библиотеки динамических ссылок во время выполнения.

1.4. Каковы пути поиска заголовочных файлов при компиляции программы?

При компиляции конфигурация пути поиска файла заголовка имеет следующие методы: (Для конкретного использования, пожалуйста, обратитесь к описанию в главе примера)

  • (1) Используйте «gcc -I incpath», чтобы указать путь поиска файла заголовка во время компиляции.
  • (2) Используйте переменную окружения "C_INCLUDE_PATH", чтобы указать путь поиска файла заголовка во время компиляции.
  • (3) Использовать путь поиска заголовка gcc по умолчанию при компиляции.

Описание общих каталогов заголовочных файлов:

/ usr / src / linux / - это путь к исходному коду ядра, а / usr / src / linux / include содержит заголовочный файл ядра, который обычно используется при компиляции модуля.

Путь / usr / include - это в основном заголовочный файл glibc, который используется для компиляции файлов пользовательского режима. Однако / usr / include также содержит папки linux / и asm /, необходимые для заголовочных файлов ядра, но эти две папки в основном используются для совместимости, а / usr все еще используется для заголовочных файлов ядра. / SRC / Linux / включить.

1.5. При настройке кросс-компилятора, каковы конфигурации пути поиска?

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

  • (1) Путь к файлу библиотеки, связанному во время компиляции:

Если не указан, это должен быть путь по умолчанию, связанный с путем установки. Если вы хотите указать, вы можете использовать опцию «LIB_PATH» при компиляции ld в binutils:

6

  • (2) Путь к заголовочному файлу, используемому во время компиляции:
  • (3) Бинарный путь инструмента, используемый во время компиляции:

Путь двоичного инструмента по умолчанию можно просмотреть с помощью следующей команды:

7

Используя указанный двоичный инструмент, вы можете использовать опцию "gcc -B":

  • (4) Путь и имя динамического загрузчика, указанного во время компиляции:

Путь и имя динамического загрузчика, жестко запрограммированного в elf, указанного в файле спецификации gcc:

8

Создайте два .c файла:

2.1, путь поиска библиотеки при связывании

Скомпилируйте say.c в libsay.so, а затем скомпилируйте libsay.so при компиляции test.c, чтобы проверить путь поиска в библиотеке, когда gcc связан.

9

  • (1) Используйте «gcc -Llibpath -llibname», чтобы указать путь поиска библиотеки ссылок:

10

(2) Используйте переменную среды "LIBRARY_PATH", чтобы указать путь поиска библиотеки ссылок:

11

(3) Используйте «SEARCH_DIR» в сценарии подключения ld, чтобы указать путь поиска библиотеки ссылок:

Используйте команду "ld -verbose", чтобы просмотреть параметр SEARCH_DIR в скрипте ссылок gcc по умолчанию. Конечно, вы также можете использовать "ld -Txxx.lds", чтобы указать скрипт связи.

12

Скопируйте libsay.so в указанный путь SEARCH_DIR и свяжите тест:

13

2.2, место поиска библиотеки динамической загрузки во время выполнения

  • (1) Используйте «gcc -Wl, -rpath», чтобы указать путь поиска библиотеки динамических ссылок во время выполнения, и жестко закодируйте в ELF-файл параметр «RPATH»:


Используйте файл readelf для просмотра жестко заданного пути поиска библиотеки времени выполнения в файле elf:

15

  • (2) Используйте переменную среды "LD_RUN_PATH", чтобы указать путь поиска библиотеки динамических ссылок во время компиляции, и жестко запишите ее в опцию "RPATH" файла ELF:

16

  • (3) Используйте переменную среды «LD_LIBRARY_PATH», чтобы указать путь поиска библиотеки динамических ссылок во время выполнения:

17

(4) Используйте файл конфигурации "/etc/ld.so.conf", чтобы указать путь поиска библиотеки динамических ссылок во время выполнения:

18

(5) Используйте путь поиска по умолчанию "/ lib, / usr / lib" библиотеки динамических ссылок во время выполнения:

19

Создайте файл .c и файл .h:

2.3 Каковы пути поиска заголовочных файлов при компиляции программы?

(1) Используйте «gcc -I incpath», чтобы указать путь поиска файла заголовка во время компиляции:

20

(2) Используйте переменную окружения "C_INCLUDE_PATH", чтобы указать путь поиска файла заголовка во время компиляции:

21

  • (3) Использовать путь поиска заголовка gcc по умолчанию при компиляции:

Метод настройки пути поиска файла заголовка Gcc по умолчанию не уточнен. Но вы можете использовать следующую команду для просмотра:

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

Эта команда запрашивает gcc, который использует препроцессор С++, а затем запрашивает, что препроцессор, где он ищет, включает.

Вы получите надежный ответ для своей конкретной настройки.

Аналогично, для препроцессора C:

Я предполагаю, что препроцессор C - cpp вместо cc1 ? В моем debian jessie $(gcc -print-prog-name=cpp) -v (правильно) дает еще один путь, который является /usr/include/x86_64-linux-gnu Если вы не хотите, чтобы ожидание ввода `gcc -print-prog-name=cc1` -v < /dev/null , перенаправьте ввод из /dev/null , поэтому `gcc -print-prog-name=cc1` -v < /dev/null . @SteveJorgensen да! Или нажмите Ctrl + D , который отправляет «конец файла» в Unix-talk. . но вы не можете нажать Ctrl + D из сценария оболочки. Это может дать неверный ответ в операционных системах, где «драйвер компилятора» (программа gcc или g++ ) переопределяет пути поиска, скомпилированные в препроцессор. Например, на компьютере, который я печатаю, этот рецепт пропускает /usr/include/x86_64-linux-gnu из пути поиска.

Кроме того, gcc будет выглядеть в каталогах, указанных после опции -I .

@totaam: проверь свой шрифт! В этом ответе используется «-I» (заглавная «глаз»), а не «-l» (строчная «ell»). -Я для <anglebracketed.h>, тогда как -iquote для "quotedfiles.h"

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

Я думаю, что было бы более полезно, если бы вы просто сказали «использовать опцию -v». Хорошо, если вы используете «-v» без файла C, который содержит несуществующий системный заголовок, вы не будете заставлять gcc перебирать все пути включения. Ключом к моему ответу является bogus.h, указанный как системный заголовок. @Jay - ты прав, это было слишком расплывчато - я объяснил, что я делал в сценарии оболочки. @Jay: опция -v сама по себе дает вывод, который не включает системный путь. gcc -v -E - < /dev/null или cpp -v < /dev/null достаточно. Вы просто должны получить препроцессор для запуска, это не имеет значения , какой вход он видит. (Пути поиска печатаются во время запуска, еще до того, как он смотрит на вход.) @zwol Спасибо за это, это намного проще, и imho заслуживает отдельного ответа.

Раздел CPP Руководство GCC показывает, что файлы заголовков могут быть расположены в следующих каталогах:

Для программ на С++ он также будет сначала смотреть в /usr/include/g ++ - v3.

Это хорошо для вашей текущей версии GCC. Реальные каталоги, в которые он смотрит, зависят от параметров, указанных при сборке gcc. Смотрите ответ Shmoopty для лучшего решения. PS: мои заголовочные файлы C ++ находятся в: /usr/include/c++/4.0.0 @ Мартин: Ты старая школа. Мои находятся в /usr/include/c++/4.2 :)

Чтобы получить GCC, чтобы распечатать полный набор каталогов, где он будет искать заголовки системы, вызовите его следующим образом:

который будет выдавать результат формы

Если у вас есть параметры -I -family в командной строке, они будут влиять на то, что распечатывается.

Эти команды выводят пути поиска по умолчанию для библиотек ссылок и внутренних компонентов компилятора; они ничего не говорят вам о заголовочных файлах.

Это каталоги, которые gcc просматривает по умолчанию для указанных файлов заголовков (учитывая, что файлы заголовков включены в chevrons < > ); 1./usr/local/include/- используется для файлов заголовков сторонних разработчиков. 2./usr/include/- используется для файлов системных заголовков.

Если в случае, если вы решите разместить свой собственный файл заголовка в месте, отличном от указанных выше каталогов, вы можете включить их следующим образом: 1. используя кавычки ( "./custom_header_files/foo.h" ) с файловым путем вместо шевронов в инструкции include. 2. с помощью ключа -I при компиляции кода. gcc -I/home/user/custom_headers/-c foo.c -p foo.o В основном, ключ -I сообщает компилятору сначала посмотреть в каталоге, указанном с ключом -I (перед тем, как он проверяет стандартные каталоги). При использовании -I-переключателя файлы заголовков могут быть включены с использованием шевронов.

Набор путей, в которых компилятор ищет файлы заголовков, может быть проверен командой: -

cpp -v

Читайте также: