Как компилировать с помощью gcc
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++, без учёта тегов препроцессора, а значит связь с объявленными функциями не будет устанавливаться.
Для чего нужна вся эта возня с промежуточными этапами?
Программы редко состоят из одного файла. Как правило исходных файлов несколько, и они объединены в проект. И в некоторых исключительных случаях программу приходится компоновать из нескольких частей, написанных, возможно, на разных языках. В этом случае приходится запускать компиляторы разных языков, чтобы каждый получил объектный файл из своего исходника, а затем уже эти полученные объектные файлы компоновать в исполняемую программу.
1. Предисловие
2. Руководство
2.1. Установка MinGW на Windows.
2.2. Прописывание пути к MinGW в Windows.
2.3. Компиляция в Windows`е
2.4. Литература по GCC
2.5. Уменьшение размера исполняемого файла.
В данной статье будет написано как пользоваться компилятором GCC и вспомогательными программами. Эта статья будет полезна для тех, кто только начал изучать С/С++, и не знает на чем и как компилировать свои первые программы, а также всем желающим научиться пользоваться этим компилятором.
2.1. Установка MinGW на Windows.
Зайдите на страничку http://sourceforge.net/projects/mingw/files/ найдите там папку Automated MinGW Installer, найдите в ней инсталлятор посвежее, скачайте его. Затем запустите его. Щелкните Next => Download and install => Next => I Agree => Current => Next => Выберите MinGW base tools, g++, MinGW Make => Next => Выберите папку куда хотите установить MinGW => Next => Install. Ждите пока скачается и установится MinGW.
2.2. Прописывание пути к MinGW в Windows.
Прописывание путей необходимо для того, чтобы для запуска компилятора не приходилось каждый раз писать полный путь к нему. Зайдите в Панель инструментов => Система => Дополнительно => Переменные среды. К переменной Path, через знак «точка с запятой» добавьте путь к папке /MinGW/bin.
2.3. Компиляция в Windows`е
1) Откройте консоль Пуск=> Выполнить=> cmd.
2) Перейдите в папку с исходником командой cd путь_к_папке_с_исходником например:
Команда PAUSE нужна для того, чтобы после компиляции консоль не закрывалась, чтобы можно было посмотреть результаты компиляции т.к. могут быть сообщения компилятора о синтаксических ошибках в исходнике. После того, как вы запишете необходимые команды в текстовой файл, надо сохранить файл и переименовать его в файл с расширением *.bat. Например make.bat. Этот файл make.bat нужно класть в папку с исходником, и для компиляции необходимо только запустить этот make.bat двойным щелчком.
2.5. Уменьшение размера исполняемого файла.
Удаление отладочной информации позволяет уменьшить размер испольняемого файла. Команда выглядит так:
strip имя_исполняемого_файла
Например:
Компиляция Objective-C в GCC
Пытаюсь выполнить компияцию кода Objective-C с помощью GCC на Windows. Для этого: 1. Установил.
Компиляция GCC в Windows
У меня в виндусе GCC для виндуса (портированный) Я хочю на нем (5,1) скомпилировать сам.
GCC:Компиляция в формат elf под Windows
Можно ли при помощи gcc создать в Windows исполняемый файл в формате elf? Если можно, то как?
gcc компиляция
Проблема возникла при компиляции проекта code::block. При build and run все работает, в папке bin.
Некоторые поправки и добавления по пунктам:
2.1 В данный момент, самая актуальная версия MinGW находится в составе MSYS2. Краткая инструкция по установке и настройке находится на главной (той же) странице официального сайта.
Компиляция gcc
Подскажите как в VS скомпилировать программу с помощью компилятора gcc
Компиляция (gcc)
Добрый вечер! После компиляции текстовый файл становится вдруг двоичным, как следствие его.
Компиляция бинарника на gcc
Задача: нужно скомпилировать сырой бинарник на gcc. Он будет грузиться по адресу 0x10000 и туда же.
Компиляция проекта в gcc
Здравствуйте, форумчане! Прошу помочь разобраться! Есть программа, представленная в 3х файлах. В.
компиляция нескольких файлов GCC
У меня есть 4 файлы. main.c #include #include «main.h» #include «file_1.h» int.
Компиляция и компоновка с помощью GNU Compiler Collection (GCC)
В состав GCC входят:
В состав GCC не входят:
Тем не менее, они необходимы для компиляции программ на Си, ввиду чего будут рассмотрены наряду с инструментами GCC. Команда запуска GCC для языка Си в общем виде выглядит следующим образом:
где в параметрах могут идти вперемешку имена входных файлов для компиляции и опции, управляющие компиляцией. В дальнейших разделах использование gcc описывается более подробно.
Схема трансляции программ написанных на Си
Трансляция программы состоит из следующих этапов:
Препроцессирование.
Трансляция в ассемблер.
Ассемблирование.
Компоновка.
Компоновщик получает на вход набор объектных файлов, соответствующим единицам трансляции, составляющим программу, подключает к ним стандартную библиотеку языка Си и библиотеки, указанные пользователем, и на выходе получает исполняемую программу.
Запуск транслятора gcc
Рассмотрим основные возможности транслятора GNU Си. В командной строке задаётся список файлов для обработки. Какие операции необходимо выполнить с файлами – зависит от суффикса имен файлов. Возможные суффиксы перечислены в таблице ниже. Если имя файла имеет нераспознанный суффикс, это имя передаётся компоновщику
Суффикс имени файла | Выполняемые действия |
---|---|
.h | Заголовочный файл на языке Си. Не должен использоваться в аргументах команды gcc. Попытка трансляции такого файла вызывает сообщение об ошибке. |
.c | Файл на языке Си. Выполняется препроцессирование, трансляция ассемблирование и компоновка. |
.i | Препроцессированный файл на языке Си. Выполняется трансляция, ассемблирование и компоновка. |
.s | Препроцессированный файл на языке Си. Выполняется трансляция, ассемблирование и компоновка. |
.S | Файл на языке ассемблера. Выполняется препроцессирование, ассемблирование и компоновка |
.o | Объектный файл. Выполняется компоновка. |
.a | Файл статической библиотеки. Выполняется компоновка. |
Действия по трансляции файла определяются для каждого указанного в командной строке файла индивидуально. Например, если в командной строке указаны имена файлов 1.c и 2.o, то для первого файла будут выполнены все шаги трансляции, а для второго – только компоновка. Исполняемый файл будет содержать результат трансляции первого файла, скомпонованный со вторым файлом и стандартными библиотеками.
Пользователь может явно задать, на какой фазе нужно остановиться. По умолчанию транслятор пытается выполнить все необходимые фазы, включая компоновку программы. Конечная фаза трансляции программы определяется для всех транслируемых за один вызов gcc файлов указанием одной из опций, перечисленных в таблице.
Например, командная строка
транслирует два файла на языке Си, объединяя их в одну программу с именем 1.
Использование стандартных библиотек языка Си
В языках Си и Си++ библиотеки состоят из двух частей:
Заголовочных файлов, содержащих объявления типов данных, констант, прототипов функций и внешних переменных, которые подключаются к исходным файлам на этапе препроцессирования, формируя единицы трансляции.
Заголовочные файлы стандартной библиотеки находятся в каталоге /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 неявно, мы получим примерно следующую командную строку:
Компоновка программы
Если исполняемая программа компонуется из нескольких единиц трансляции, компоновщик использует свои правила видимости имён, которые приведены ниже:
Последнее правило можно продемонстрировать на следующем примере. Предположим, что в трёх файлах определена переменная var следующим образом:
Если все три единицы компиляции объединяются в одну программу, то переменная var каждого из трёх файлов будет располагаться по одному и тому же адресу, и каждая из трёх функций будет работать, по сути, с общей переменной. Чтобы предотвратить такое слияние переменных можно использовать явную инициализацию переменной var, тогда компоновщик выдаст сообщение об ошибке как показано ниже.
Видимость глобальных переменных при компоновке можно также регулировать с помощью спецификаторов класса памяти extern и static. Спецификатор extern запрещает компилятору выделять память под переменную в данном модуле. Спецификатор static локализует область видимости переменной единицей трансляции, в которой она определена.
Программы из нескольких единиц трансляции
Как правило, сложные программы состоят из нескольких исходных файлов, которые объединяются компоновщиком. При написании таких программ полезно следовать следующим рекомендациям
При группировке функций и переменных по исходным файлам логически сильно связанные функции объединяются в один исходный файл. Данная рекомендация соответствует способу реализации на Си парадигмы модульного программирования, предполагающего разбиение программы на независимые части – модули. Например, если речь идет о программе-редакторе табличных данных, функции обеспечивающие сохранение таблицы в файл, могут быть помещены в один исходный файл, функции, которые выводят на экран содержимое таблицы, – в другой файл, функции, которые анализируют ввод пользователя, – в третий.
Чем больше переменных объявлено в единице компиляции с классом памяти static вместо класса памяти по умолчанию, тем лучше. Программа может быть легче модифицирована, если доступ к данным всегда происходит с помощью вызовов функций. Чем меньше «чужих» переменных использует некоторая единица компиляции, тем она проще для понимания.
В заголовочном файле помещаются макроопределения и типы данных, являющиеся интерфейсом данной единицы компиляции, то есть необходимые для использования функций и переменных этой единицы компиляции. С классом памяти extern помещаются необходимые переменные и прототипы функций, объявленные в соответствующей единице компиляции.
В заголовочный файл никогда не помещаются определения переменных с классом памяти, отличным от класса extern, и тела функций. В заголовочный файл никогда не помещаются прототипы функций с классом памяти static. Если некоторый тип или константа используются только в теле какой-либо функций и не требуется для корректного использования функциональности данной единицы компиляции другими модулями, этот тип или константа также не помещаются в заголовочный файл.
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.
Использование GCC
Введение
Если имя ‘a.out’ кажется вам не очень выразительным, то вы наверняка захотите указать другое, делается это так:
Языковые стандарты
Язык C
GCC поддерживает три версии стандарта C, хотя последняя версия поддерживается лишь частично.
Язык C++
Предупреждения компилятора
Предупреждения компилятора это текстовые сообщения, которые может выдавать компилятор при обработке ваших исходных кодов. Эти сообщения указывают на некоторые нелогичные или не строгие по отношению к существующим стандартам языков программирования места в программе. Они могут сигнализировать о потенциальных ошибках или, попросту говоря, о сомнительных с точки зрения компилятора конструкциях.
Предупреждения компилятора это очень полезный механизм, позволивший программистам избежать многих незаметных на первый взгляд ошибок, которые могли проявиться только после запуска уже скомпилированной программы (возможно, много лет спустя после первого запуска). Его, безусловно, нужно использовать, и относиться к предупреждениям следует максимально внимательно. Многие авторитетные книги по C++, написанные в жанре сборников советов (Скотта Мейерса, Герба Саттера, Андрея Александреску, Стивена Дьюхерста), содержат совет компилировать программу на максимально строгом уровне предупреждений и добиваться того, чтобы компилятору не к чему было придраться.
Далее в примерах при использовании GCC мы не будем указывать опции, включающие предупреждения, чтобы не загромождать код. Мы даём честное слово, что на практике всегда используем максимальный уровень предупреждений и рекомендуем вам делать то же.
Отладочная информация
Сложно представить себе программу, которая выполняет что-то осмысленное и которая была написана с первого раза без единой ошибки. Хуже того, иногда может оказаться, что давно и успешно работающая программа также содержит изъяны. Установить источник проблем помогает отладочная информация, которую компилятор может добавить в каждый объектный файл. Она включает в себя указание на типы данных, использованные в программе, и соответствие объектного кода строкам текста исходного файла. Отладочная информация, сгенерированная GCC, предполагается для использования в первую очередь отладчиком GDB (GNU debugger, отладчик GNU), но не только им.
Оптимизация
Если с предупреждениями компилятора всё ясно (действует принцип «чем больше, тем лучше»), количество отладочной информации также, в основном, контролируется программистом в соответствии с его целями (доведение программы до работоспособного состояния или изготовление окончательной версии программы для передачи заказчику), то с оптимизацией дело обстоит намного сложней. Оптимизация это процесс улучшения производительности программы, в том числе, увеличение скорости её работы и снижение объёма занимаемой памяти (эти две цели, между прочим, иногда противоречат друг другу). GCC поддерживает большой набор ключей оптимизации и часть из них объединены в группы (как и в случае с предупреждениями), что позволяет активировать их также по групповому принципу.
Возникает закономерный вопрос: если есть ключи, ускоряющие программу, почему бы не включить их все разом и получить самый быстрый код? Проблема состоит в том, что эффективность значительной части средств для увеличения производительности программы сильно зависит от характера оптимизируемого кода и архитектуры аппаратной платформы. Оптимизация, таким образом, превращается в искусство отражения конкретных особенностей реализации и имеющегося аппаратного обеспечения на существующие ключи компилятора. Кроме того, результат этой деятельности, вероятно, окажется плохо или просто непереносимым. Неудачный выбор ключей, с другой стороны, может обеспечить гарантированное и весьма существенное падение производительности.
Мы упомянем только несколько ключей, обозначающих группы опций оптимизации, которые были подобраны разработчиками GCC и позволяют улучшить производительность во многих случаях, оставив рассмотрение отдельных опций и их связь с особенностями аппаратного обеспечения для более специализированных публикаций.
Раздельная трансляция
Вернёмся от увлекательных особенностей C++ к использованию GCC. Оба указанных выше варианта компилируются и линкуются в один исполняемый файл одинаково, при помощи простого перечисления: Стоит отметить удобную возможность использования шаблонов имён (wildcards), чтобы избежать полного перечисления файлов: даст аналогичный приведённому выше результат.
Библиотеки
Раздельная трансляция файлов делает доступным систематическое повторное использование кода: набор объектных файлов, содержащих функции, которые могут быть использованы в разных программах, собирается в библиотеку (программного кода), представляющую собой единый файл, который содержит весь код объектных с некоторой дополнительной информацией (наподобие индекса в настоящих библиотеках). Последнее может ускорять процесс линковки при использовании библиотеки по сравнению с обычной сборкой программы из большого числа объектных файлов. Информация об исходных объектных файлах также сохраняется.
Бесспорное преимущество динамических библиотек состоит в том, что если несколько программ используют одну библиотеку, то она загружается в память только один раз. Иными словами, сразу несколько программ могут (и будут) использовать один загруженный экземпляр библиотеки «одновременно». В то же время использование статической библиотеки заставит добавлять части её кода в каждый исполняемый файл по отдельности. Обновление динамической библиотеки потребует перезапуска, использующих её программ, статической — их перелинковки (что обычно занимает немало времени).