Compiling c code in cpp

Unix как IDE: Компиляция

Под Unix существует множество компиляторов и интерпретаторов, но здесь мы будем обсуждать лишь gcc как средство компиляции C-кода, и коротко коснемся использования perl в качестве примера интерпретатора.

GCC

GCC — это набор компиляторов, обладающий очень почтенным возрастом и распространяемый под лицензией GPL. Он известен как инструмент работы с программами на C и C++. Свободная лицензия и повсеместная распространенность на Unix-подобных системах стали залогом его неизменной популярности, хотя есть и более современные альтернативы, использующие инфраструктуру LLVM, такие как Clang.

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

Здесь я не буду обсуждать использование make-файлов, хотя они наверняка понадобятся для любого проекта сложнее, чем в один файл. Make-файлов я коснусь в следующей статье о средствах автоматизации сборки.

Компиляция и сборка объектного кода

Объектный код компилируется вот такой командой:

$ gcc -c example.c -o example.o 

Если код верен, будет создан нелинкованный двоичный объектный файл под именем example.o в текущей папке, или выведены сообщения о проблемах. Заглянуть внутрь полученного файла и увидеть его содержимое на языке ассемблера можно так:

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

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

$ gcc -c -S example.c -o example.s 

Вывод ассемблерного кода может быть полезно совместить с выводом самого исходника, чего можно добиться, набрав:

$ gcc -c -g -Wa,-a,-ad example.c > example.lst 

Препроцессор

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

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

Связывание объектов

Один или несколько объектных файлов могут быть связаны в соответствующий исполняемый файл:

В этом примере gcc просто вызывает ld, линковщик GNU. Команда создаст исполняемый файл по имени example .

Компиляция, сборка и связывание

Все вышеперечисленное может быть выполнено в один шаг при помощи команды:

Этот способ проще, но компиляция объектов по отдельности дает некоторый выигрыш в производительности: не нужно компилировать не изменявшийся код, но об этом мы поговорим в следующей статье.

Включение внешних файлов и связывание

Файлы C и заголовочные файлы могу быть явно включены в компиляцию при помощи параметра -l:

$ gcc -I/usr/include/somelib.h example.c -o example 

Аналогично, если код нужно динамически связать с уже скомпилированной системной библиотекой, доступной в одной из системных папок ( /lib или /usr/lib ), например, ncurses, этого можно добиться использованием ключа -l:

$ gcc -lncurses example.c -o example 

Если в процессе компиляции внешних связей много, имеет смысл внести их в переменные окружения:

$ export CFLAGS=-I/usr/include/somelib.h $ export CLIBS=-lncurses $ gcc $CFLAGS $CLIBS example.c -o example 

Кстати, Makefile затем и создан, чтобы избавить нас от беспокойства о таких мелочах.

Читайте также:  How to use proxy in python

План компиляции

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

$ gcc -v -c example.c -o example.o 

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

$ gcc -### -c example.c -o example.o 

Очень полезно посмотреть, какие действия gcc предпринимает без нашего ведома, кроме того, так мы можем выявить нежелательные шаги при компиляции.

Расширенный вывод сообщений об ошибках

Существует возможность добавить ключи -Wall и/или -pedantic, чтобы gcc предупреждал нас о случаях, которые не обязательно являются ошибками, но могут ими быть:

$ gcc -Wall -pedantic -c example.c -o example.o 

Удобно включать такие опции в Makefile или в определении makeprg для Vim, так как они отлично сочетаются с окном quickfix, и помогают писать читабельный, совместимый и безошибочный код.

Профилирование процесса компиляции

Вы можете включить опцию -time, чтобы gcc отображал в тексте вывода время выполения каждого из шагов:

$ gcc -time -c example.c -o example.o 

Оптимизация

Gcc имеет ключи оптимизации, указав которые можно попросить его создавать более эффективный объектный код и связанные бинарники за счет увеличения времени компиляции. Я считаю -O2 золотой серединой для выпускаемого кода:

Подобно любой команде Bash, все это можно вызывать прямо из Vim:

Интерпретаторы

Подход к интерпретируемому коду в Unix-системах иной. В своих примерах я буду использовать Perl, но те же принципы применимы для кода, например, на Python или Ruby.

Inline-код

Можно строку Perl-кода прямо на исполнение интерпретатору любым из перечисленных ниже способов Первый, наверное, самый простой и общеупотребительный способ работы с Perl; второй использует синтаксис heredoc, а третий — это классический конвейер Unix.

$ perl -e 'print "Hello world.\n";' $ perl  

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

Можно проверить синтаксис кода без его выполнения с помощью ключа -c:

Порой хочется использовать скрипт подобно любому исполняемому бинарнику, не беспокоясь о том, что он из себя представляет. Для этого в скрипт добавляют первой строкой так называемый "shebang", указывающий путь к интерпретатору, которому следует передать на исполнение данный файл.

#!/usr/bin/env perl print "Hello, world.\n"; 

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

Затем файл можно вызывать напрямую, без указания интерпретатора:

Вся эта кухня так здорово работает, что многие стандартные утилиты Linux-систем, такие как adduser, в действительности являются скриптами на Perl или Python.

В следующей публикации я расскажу о методах работы с make для сборки проектов, сравнимых с привычными IDE.

Продолжение следует.

Источник

Оцените статью