gcc компилятор что это
Национальная библиотека им. Н. Э. Баумана
Bauman National Library
Персональные инструменты
GNU Compiler Collection
Содержание
Обзор
В настоящее время GCC поддерживается группой программистов со всего мира. GCC является лидером по количеству процессоров и операционных систем, которые он поддерживает.
Будучи официальным компилятором системы GNU, GCC также является главным компилятором для сборки ряда других операционных систем; среди них — различные варианты Linux и Berkeley Software Distribution|BSD, а также ReactOS, Mac OS X, OpenSolaris, NeXTSTEP, BeOS и Haiku.
GCC часто выбирается для разработки программного обеспечения, которое должно работать на большом числе различных аппаратных платформ. Различия между «родными» для каждой из аппаратных платформ компиляторами приводят к трудностям при разработке кода, который бы корректно компилировался разными компиляторами, а кроме того, при использовании различных компиляторов сильно усложняются сборочные скрипты, которые должны собирать ПО для всех аппаратных платформ. При использовании GCC для компиляции кода под разные платформы будет использован один и тот же синтаксический анализатор. Поэтому если удалось собрать программу для одной из целевых платформ, то велика вероятность, что программа нормально соберётся и для других платформ.
Языки
В версии 4.1.1 (выпущенной 24 мая 2006 года стандартный компилятор включал в себя front-end’ы для языков:
Front end для CHILL был добавлен ранее, но из-за недостаточной поддержки был исключён из набора. До выхода версии 4.0 front-end’ом для Fortran был G77, который поддерживал лишь FORTRAN 77. В новых версиях G77 был исключён в пользу нового GFortran frontend, который поддерживает Fortran 95.
Также существуют front-end’ы для Паскаль (язык программирования)|Pascal, D, Modula-2, Modula-3, Mercury, VHDL и PL/I.
Архитектуры
Список поддерживаемых GCC (для версии 4.3) процессоров включает в себя:
Менее известные процессоры, поддерживаемые в стандартном релизе:
Дополнительные типы архитектур и процессоров, которые поддерживаются версиями GCC, но поддержкой которых занимаются сторонние организации (не Фонд свободного программного обеспечения):
Структура
Внешний интерфейс GCC является стандартом для компиляторов на платформе UNIX. Пользователь вызывает управляющую программу, которая называется gcc. Она интерпретирует аргументы командной строки, определяет и запускает для каждого входного файла свои компиляторы нужного языка, запускает, если необходимо, ассемблер и компоновщик.
Компилятор каждого языка является отдельной программой, которая получает исходный текст и порождает вывод на язык ассемблера|языке ассемблера. Все компиляторы имеют общую внутреннюю структуру: front end, который производит синтаксический разбор и порождает абстрактное синтаксическое дерево, и back end, который конвертирует дерево в Register Transfer Language (RTL), выполняет различные оптимизации, затем порождает программу на языке ассемблера, используя архитектурно-зависимое сопоставление с образцом.
Отладка программ, скомпилированных с помощью GCC
Главным инструментом для отладки программ, скомпилированных с помощью GCC, является GNU Debugger (gdb). Существуют также узкоспециализированные средства для отладки:
Лицензия
GCC версии 4.2.1 стал последним релизом, выпущенным под GNU General Public License версии 2. Все последующие версии лицензируются по [[GNU General Public License#GPL v3|GPL версии 3].
Критика
Обзор GCC 4.8
С выпуском GCC 4.8.0 разработчики набора компиляторов GNU Compiler Collection завершили переход на C++ в реализации GCC. Работа по переводу кодовой базы на C++ продолжалась c 2008 года, и теперь подошла к концу. Миграция на C++ означает, что теперь для сборки GCC из исходников обязательно требуется компилятор С++ 2003. Ричард Столлман написал первый вариант GCC в 1985 году на непереносимом диалекте языка Паскаль. В 1987 году компилятор был переписан на языке Си, и в таком виде существовал до 2013 года.В новой версии GCC 4.8 улучшена производительность, реализован новый уровень оптимизации –Og для сверхбыстрой компиляции почти без оптимизаций. Добавлены детектор ошибок в памяти Address Sanitizer от компании Google и детектор гонок данных Thread Sanitizer, который обнаруживает совместный доступ к одним и тем же данным из различных нитей многопоточного приложения. Более подробно о нововведениях см. release notes.Детектор Address Sanitizer можно использовать на платформах Linux (IA-32, x86-64, x32, PowerPC, PowerPC64) и Darwin (x86-64), при этом скорость работы программы замедляется примерно в два раза.Детектор Thread Sanitizer замедляет скорость примерно в 10 раз.Кроме того, в GCC 4.8 улучшена поддержка C++11 и появилась поддержка архитектуры AArch64(ARM64), присутствующей в процессорах с набором команд ARMv8, хотя на рынке пока нет устройств с таким набором команд.GCC — официальный компилятор системы GNU, он также является главным компилятором для ряда других операционных систем, в том числе разных вариантов Linux и BSD, Mac OS X, ReactOS, BeOS и проч. Подробнее о причинах миграции на C++ и конкретно о внесённых изменениях см. в GCC Wiki: C++ Conversion. Вкратце, причина в популярности языка C++ и более чистом коде на «плюсах».
К счастью, переход на C++ практически не отразился на производительности компиляторов GCC. [Источник 2]
Установка GCC на Debian Linux
Gcc компилятор что это
Вы можете использовать gcc для компиляции программ в объектные модули и для компоновки полученных модулей в единую исполняемую программу. Компилятор способен анализировать имена файлов, передаваемые ему в качестве аргументов, и определять, какие действия необходимо выполнить. Файлы с именами типа name.cc (или name.C ) рассматриваются, как файлы на языке C++, а файлы вида name.o считаются объектными (т.е. внутримашинным представлением).
Здесь строка compile-options указывает возможные дополнительные опции компиляции.
После компоновки будет создан исполняемый файл F, который можно запустить с помощью команды:
где строка arguments определяет аргументы командной строки вашей программы.
В процессе компоновки очень часто приходится использовать библиотеки. Библиотекой называют набор объектных файлов, сгруппированных в единый файл и проиндексированных. Когда команда компоновки обнаруживает некоторую библиотеку в списке объектных файлов для компоновки, она проверяет, содержат ли уже скомпонованные объектные файлы вызовы для функций, определенных в одном из файлов библиотек. Если такие функции найдены, соответствующие вызовы связываются с кодом объектного файла из библиотеки.
в начале программы. Часть `=value’ может быть опущена, в этом случае значение по умолчанию равно 1.
Использовать file-name в качестве имени для создаваемого gcc файла (обычно это исполняемый файл).
Использовать при компоновке указанную библиотеку.
Вывод сообщений о всех предупреждениях или ошибках, возникающих во время трансляции программы.
Устанавливает оптимизацию уровня 1. Оптимизированная трансляции требует несколько больше времени и несколько больше памяти для больших функций. Без указания опций `-O’ цель компилятора состoит в том, чтобы уменьшить стоимость трансляции и выдать ожидаемые результаты при отладке. Операторы независимы: если вы останавливаете программу на контрольной точке между операторами, то можете назначить новое значение любой переменной или поставить счетчик команд на любой другой оператор в функции и получить точно такие результаты, которые вы ожидали от исходного текста. С указанием `-O’ компилятор пробует уменьшить размер кода и время исполнения.
Без оптимизации. Если вы используете многочисленные `-O’ опции с номерами или без номеров уровня, действительной является последняя такая опция.
GNU Compiler Collection, первые шаги
Эта заметка призвана на простых примерах познакомить начинающего nix-разработчика с инструментами GNU, в частности с компилятором GCC.
С его помощью мы и создадим простейшую программу. По большому счету все, как обычно. Заводим специальную папку, в которой будет размещаться проект.
Создаем в ней файл с именем: hello.c
Открываем файл в любом текстовом редакторе и пишем простейший код:
#include
int main(void)
<
printf(«Hello world!»);
return(0);
>
Сохраняем файл и выполняем команду: gcc hello.c
В созданной нами папке появился новый файл — a.out, это название присваивается по умолчанию, если специально не задано другого.
И радуемся в связи с первой написанной программой в линуксе!
Идем далее. При запуске исполняемого файла, если мы укажем только его название, система будет искать его в каталогах /usr/bin и /usr/local/bin, и, естественно, не найдет. Первый из них предназначен для размещения стабильных версий программ, как правило, входящих в дистрибутив Linux. Второй – для программ, устанавливаемых самим пользователем (за стабильность которых никто не ручается). По умолчанию, при сборке программы, устанавливаются в каталог /usr/local/bin.
Флаги используемые при компиляции
Название получаемого файла такое же, но компилятор изменяет расширение .c на .o (но указать можно и вручную).
Флаг -x используем, если создаётся объектный файл из исходника, уже обработанного препроцессором (например такого, какой мы получили выше), мы должны обязательно указать явно, что компилируемый файл является файлом исходного кода, обработанный препроцессором, и имеющий теги препроцессора. В противном случае он будет обрабатываться, как обычный файл C++, без учёта тегов препроцессора, а значит связь с объявленными функциями не будет устанавливаться.
Для чего нужна вся эта возня с промежуточными этапами?
Программы редко состоят из одного файла. Как правило исходных файлов несколько, и они объединены в проект. И в некоторых исключительных случаях программу приходится компоновать из нескольких частей, написанных, возможно, на разных языках. В этом случае приходится запускать компиляторы разных языков, чтобы каждый получил объектный файл из своего исходника, а затем уже эти полученные объектные файлы компоновать в исполняемую программу.
Gcc компилятор что это
Что такое gcc и g++? Если задать этот вопрос людям, которые хотя бы отдаленно знакомы с Linux, то наверняка можно получить следующий ответ: «gcc — это компилятор языка Си, а g++ — компилятор С++». В каком-то смысле это будет правильный ответ. А если этим же людям задать другой вопрос: «А вы знаете, что на самом деле не существует компилятора g++, а gcc — это не Си-компилятор?» Бьюсь об заклад, что в 99% случаев можно услышать в ответ обвинения в ламерстве, дебилизме, параличе мозга, врожденной болезнью Дауна и пр. Ты тоже входишь в эти 99%? Тогда читай ниже.
Несмотря на то, что на момент написания этой статьи уже появилась версия GCC 3.3.2, речь однако пойдет о версии 2.96. Именно эта версия установлена на моем стареньком Red Hat 7.1 и меня совершенно не втыкает выкачивать около 30 МБ (если не ошибаюсь, именно столько весит архив GCC 3.3.2), т. к. 2.96 меня пока что полностью устраивает. Я просмотрел список изменений и дополнений, прошедших с версии 2.96 и не заметил ни чего существенного, чтобы могло бы повлиять на ход этой статьи. Но в качестве домашнего задания, я оставляю тебе проверить все сказанное здесь для других версий компилятора и сообщить о результатах мне.
Подопытная крыса
Создадим простейшую тестовую программку на С++ и сохраним ее под именем hello.cpp:
Выполним сначала компиляцию с помощью g++:
Все проходит без ошибок, в текущей директории образуется выполняемый файл hello. Теперь попробуем произвести компиляцию этой же программы с помощью gcc:
Компиляция не проходит, на экран вываливаются ошибки вроде тех, что показаны на скриншоте (см. рис. 1).
Рис.1. gcc отказывается компилить CPP-программу
Между тем gcc отлично справляется с любой Си-прогой. Так, значит, gcc не понимает программ на C++, а только Си-программы? Значит g++ и gcc два разных компилятора? Зачем же я тогда завел весь этот разговор? Подожди, подожди, давай обратимся к man.
Если ты не умеешь читать
Откроем man g++. Прямо с первых строк, в разделе описания читаем (я привожу сразу перевод):
«Компиляторы Си и Си++ объединены; g++ — это скрипт, который вызывает gcc с опциями языка С++. gcc пропускает входные файлы через одну или более из четырех стадий: препроцессирование, компиляцию, ассемблирование и линковку».
Поэтому обратимся лучше к самому свежему мануалу, а именно к «GCC 3.3.2 Manual», который можно найти на официальном сайте http://gcc.gnu.org/onlinedocs/. Вот что там можно прочитать (далее перевод):
«Несколько версий компиляторов объединены (C, C++, Objective-C, Ada, Fortran, Java). Вот почему мы используем название «GNU Compiler Collection». GCC может компилировать программы написанные на любом из этих языков. Ada, Fortran, Java описаны в отдельных мануалах.
Когда ссылаются на C++ компиляцию, обычно называют компилятор «G++». Поскольку есть только один компилятор, будет точным называть его «GCC» вне зависимости от языка; однако термин «G++» более полезен, когда ударение стоит на компиляции С++ программ».
Если ты так ничего и не понял
Как же так, мы ведь вначале статьи на эксперименте убедились, что gcc и g++ работают по-разному, а в мануале сказано, что есть только один компилятор GCC? Давай не будем делать поспешных выводов, а лучше снова обратимся к man. В разделе FILES обоих манов («man gcc» и «man g++») мы обнаружим интересную деталь (см. рис. 2), оказывается, что для компиляции Си-программ используется компилятор cc1, а для программ на C++ как gcc, так и g++ вызывают cc1plus! Так вот они настоящие компиляторы! Там же мы увидим имена файлов препроцессора, линкера и пр., причем все эти файлы являются составной частью пакета GCC! Значит, gcc и g++ просто вызывают указанные файлы, выполняя поочередно все этапы создания бинарника (препроцессирование, компиляцию, ассемблирование и линковку)!
Рис.2. Вот они настоящие компиляторы!
Сравнение работы gcc и g++
Хорошо, мы убедились, что gcc и g++ одинаково смогли справиться с задачей, но может быть на внутреннем уровне они отличаются? Может быть процесс создания исполняемого файла в них идет по-разному, а ведь это в свою очередь может свидетельствовать о том, что это совершенно независимые компиляторы? Что ж попытаемся выяснить и это. Естественно мы не будем рыться в исходниках GCC, т. к. компилятор это не программка в сотню строк и одной бутылочки, чтобы разобраться, явно не хватит. Для того чтобы увидеть, что происходит в действительности, существует замечательный ключик –v. Результат компиляции с использованием этого ключа показан на рисунке 3.
Рис.3. Результат компиляции с опцией –v
Чтобы сравнить выходные данные gcc и g++, я сохранил результаты вывода в текстовые файлы:
gcc –v –o hello hello.cpp 2>tmp_gcc.txt
И воспользовавшись утилитой diff, выяснил, что выходные данные оказались практически одинаковыми. Отличие заключается лишь в именах темповых файлов, например в одном случае: /tmp/cczEKZcb.ii, а в другом: /tmp/ccdVfS2M.ii — но это само собой разумеющиеся отличия! Следовательно, процесс создания исполняемого файла на «внутреннем» уровне в обоих случаях протекает совершенно одинаково. Давай рассмотрим этот процесс в деталях.
gcc и g++ по косточкам
Далее я привожу пошаговый разбор той информации, которую выводят gcc и g++ с использованием ключа –v (tmp_gcc.txt или tmp_g++):
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/2.96/specs
gcc version 2.96 20000731 (Red Hat Linux 7.1 2.96-81)
Эти строки говорят о том, что происходит чтение файла specs, на основании которого будет строиться дальнейшая компиляция, а также показывается номер версии компилятора gcc. Еще раз отмечаю: gcc и g++ выводят совершенно идентичную информацию!
Первым вызывается препроцессор cpp0, он заменяет в программе комментарии пробелами, вставляет файлы #include, преобразует директивы #define, #ifdef, #undef и пр. В результате препроцессирования образуется темповый файл /tmp/cczEKZcb.ii, необходимый для дальнейшей компиляции.
ignoring nonexistent directory «/usr/local/include»
ignoring nonexistent directory «/usr/i386-redhat-linux/include»
#include «. » search starts here:
#include search starts here:
End of search list.
Теперь в работу вступает компилятор С++: cc1plus, который на основании файла /tmp/cczEKZcb.ii, полученного на этапе препроцессирования, создает исходник на ассемблере /tmp/ccw4l1Ud.s.
Далее наступает этап ассемблирования, для чего вызывается стандартный ассемблер as, который создает объектный файл /tmp/cc5T4w7y.o.
Завершается все линковкой. Линковщик collect2 объединяет объектные файлы с необходимыми разделяемыми библиотеками и создает исполняемый файл. Ключ –L показывает, в каких директориях происходит поиск библиотек.
Ну и какие выводы?
История GCC
Первая версия gcc была создана основателем «Free Software Foundation» Ричардом Столманом в 1987 году. Первоначально аббревиатура GCC означала «GNU C Compiler», т. к. gcc тогда компилировал только Си-программы. Теперь GCC расшифровывается, как «GNU Compiler Collection», т. к. включает в себя множество компиляторов: C, C++, Objective-C, Ada, Java и пр. Огромный вклад в развитие GCC внесла компания Cygnus под руководством Майкла Тиманна (Michael Tiemann). В 1997 между Cygnus и «Free Software Foundation», из-за некоторых разногласий произошел раскол, и Cygnus создала свою версию компилятора EGCS. Однако в 1999 году конфликт был исчерпан и проект EGCS слился с GCC. Последнюю версию компилятора GCC всегда можно найти на официальном FTP-сервере: ftp://ftp.gnu.org/gnu/gcc/.
Особенность G++
В статье я не сказал об одной мелкой детали. G++ всегда по умолчанию линкует библиотеку математических подпрограмм libm (по крайней мере, версия GCC 2.96). По этой причине программы скомпилированные g++ получаются всегда немного большего размера, чем те же программы полученные с помощью gcc. Если при компиляции с помощью gcc в командной строке указать флаг –lm будет получен тот же эффект.
Unix как IDE: Компиляция
Под Unix существует множество компиляторов и интерпретаторов, но здесь мы будем обсуждать лишь gcc как средство компиляции C-кода, и коротко коснемся использования perl в качестве примера интерпретатора.
GCC — это набор компиляторов, обладающий очень почтенным возрастом и распространяемый под лицензией GPL. Он известен как инструмент работы с программами на C и C++. Свободная лицензия и повсеместная распространенность на Unix-подобных системах стали залогом его неизменной популярности, хотя есть и более современные альтернативы, использующие инфраструктуру LLVM, такие как Clang.
Основной исполняемый файл gcc лучше представлять не как компилятор в привычном понимании, а слой абстракции над множеством отдельных инструментов программирования, выполняющих парсинг кода, компиляцию, линковку и другие действия. Это значит, что с его помощью можно не просто получить работающий бинарник из кода на C, но детально исследовать все шаги этого сложного процесса, и при необходимости подстроить его под свои нужды.
Здесь я не буду обсуждать использование make-файлов, хотя они наверняка понадобятся для любого проекта сложнее, чем в один файл. Make-файлов я коснусь в следующей статье о средствах автоматизации сборки.
Компиляция и сборка объектного кода
Объектный код компилируется вот такой командой:
Если код верен, будет создан нелинкованный двоичный объектный файл под именем example.o в текущей папке, или выведены сообщения о проблемах. Заглянуть внутрь полученного файла и увидеть его содержимое на языке ассемблера можно так:
Как вариант, можно попросить gcc сразу показать итоговый ассемблерный код при помощи параметра -S:
Вывод ассемблерного кода может быть полезно совместить с выводом самого исходника, чего можно добиться, набрав:
Препроцессор
Препроцессор C (cpp) обычно используется для подключения заголовочных файлов и определения макросов. Это стандартная часть процесса компиляции gcc, но можно просмотреть генерируемый им код, вызвав cpp напрямую:
Исходный код будет выведен в конечном виде, готовым к компиляции, с замененными макросами и подстановкой включаемых внешних файлов.
Связывание объектов
Один или несколько объектных файлов могут быть связаны в соответствующий исполняемый файл:
Компиляция, сборка и связывание
Все вышеперечисленное может быть выполнено в один шаг при помощи команды:
Этот способ проще, но компиляция объектов по отдельности дает некоторый выигрыш в производительности: не нужно компилировать не изменявшийся код, но об этом мы поговорим в следующей статье.
Включение внешних файлов и связывание
Файлы C и заголовочные файлы могу быть явно включены в компиляцию при помощи параметра -l:
Аналогично, если код нужно динамически связать с уже скомпилированной системной библиотекой, доступной в одной из системных папок ( /lib или /usr/lib ), например, ncurses, этого можно добиться использованием ключа -l:
Если в процессе компиляции внешних связей много, имеет смысл внести их в переменные окружения:
Кстати, Makefile затем и создан, чтобы избавить нас от беспокойства о таких мелочах.
План компиляции
Чтобы посмотреть подробности внутренней кухни gcc, можно добавить ключ -v, и план компиляции будет выведен в стандартный поток вывода ошибок:
Если нет нужды генерировать объектные или исполняемые файлы, то для аккуратности можно использовать -###:
Очень полезно посмотреть, какие действия gcc предпринимает без нашего ведома, кроме того, так мы можем выявить нежелательные шаги при компиляции.
Расширенный вывод сообщений об ошибках
Существует возможность добавить ключи -Wall и/или -pedantic, чтобы gcc предупреждал нас о случаях, которые не обязательно являются ошибками, но могут ими быть:
Удобно включать такие опции в Makefile или в определении makeprg для Vim, так как они отлично сочетаются с окном quickfix, и помогают писать читабельный, совместимый и безошибочный код.
Профилирование процесса компиляции
Вы можете включить опцию -time, чтобы gcc отображал в тексте вывода время выполения каждого из шагов:
Оптимизация
Подобно любой команде Bash, все это можно вызывать прямо из Vim:
Интерпретаторы
Подход к интерпретируемому коду в Unix-системах иной. В своих примерах я буду использовать Perl, но те же принципы применимы для кода, например, на Python или Ruby.
Inline-код
Можно строку Perl-кода прямо на исполнение интерпретатору любым из перечисленных ниже способов Первый, наверное, самый простой и общеупотребительный способ работы с Perl; второй использует синтаксис heredoc, а третий — это классический конвейер Unix.
Конечно, в будничной жизни мы храним код в файле, который можно вызвать прямо вот так:
Можно проверить синтаксис кода без его выполнения с помощью ключа -c:
Порой хочется использовать скрипт подобно любому исполняемому бинарнику, не беспокоясь о том, что он из себя представляет. Для этого в скрипт добавляют первой строкой так называемый «shebang«, указывающий путь к интерпретатору, которому следует передать на исполнение данный файл.
Скрипту после этого можно ставить атрибут исполняемого файла вызовом chmod. Также хорошим тоном считается переименовать файл, убрав расширения, поскольку он теперь считается почти настоящим исполняемым файлом:
Затем файл можно вызывать напрямую, без указания интерпретатора:
Вся эта кухня так здорово работает, что многие стандартные утилиты Linux-систем, такие как adduser, в действительности являются скриптами на Perl или Python.
В следующей публикации я расскажу о методах работы с make для сборки проектов, сравнимых с привычными IDE.