Gcc windows как пользоваться

GCC — компиляция C/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. Краткая инструкция по установке и настройке находится на главной (той же) странице официального сайта.

2.5 Параметр -s (или —strip-all ) позволяет максимально уменьшить размер исполняемого файла, удалив из него всю информацию о символах и релокации.

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

Компиляция gcc
Подскажите как в VS скомпилировать программу с помощью компилятора gcc

Компиляция (gcc)
Добрый вечер! После компиляции текстовый файл становится вдруг двоичным, как следствие его.

Компиляция бинарника на gcc
Задача: нужно скомпилировать сырой бинарник на gcc. Он будет грузиться по адресу 0x10000 и туда же.

Компиляция проекта в gcc
Здравствуйте, форумчане! Прошу помочь разобраться! Есть программа, представленная в 3х файлах. В.

компиляция gcc и MSVC
Привет всем. Объясните, почему один и тот же код #include «stdafx.h» #include .

компиляция нескольких файлов GCC
У меня есть 4 файлы. main.c #include #include «main.h» #include «file_1.h» int.

Использование GCC

Введение

Один из самых распространённых компиляторов с языков C и C++ на многих современных Unix-подобных системах, таких как GNU/Linux, члены семейства систем BSD и Mac OS X, это GCC. Первоначально эта аббревиатура расшифровывалась как GNU C Compiler (компилятор языка C от организации GNU), в свою очередь GNU является «рекурсивным акронимом», то есть расшифровывается через самое себя: GNU=GNU is Not UNIX, имея ввиду свою открытость в отличие от оригинальных систем UNIX (таких как UNIX System V от компании AT&T и BSD UNIX, разрабатывавшемся в Университете Беркли, Калифорния). После его появления в 1987 году к GCC был дописан ряд фронт-ендов (front-ends, трансляторов исходных кодов во внутреннее представление GCC, рассчитанное на дальнейшую трансляцию в исполняемый двоичный код бек-ендом, back-end) для других языков программирования:

  • C++,
  • Objective-C,
  • Objective-C++,
  • Fortran,
  • Java,
  • Ada,

и некоторых других, а также реализации стандартных библиотек этих языков. В связи с этим участники этого проекта предложили расшифровывать название как GNU Compiler Collection (коллекция компиляторов GNU).

Простейший способ задействовать средства GCC — выполнить в командной строке команду: ( $ здесь и далее означает приглашение командной строки, а курсивом выделены строки, которые должны быть чем-то замещены, причем сама строка указывает на то, чем). Файл с указанным именем должен содержать исходный код на одном из языков, поддерживаемых GCC. Расширение файла может подсказать, какой именно язык используется. В частности, для C++ можно использовать такие расширения:

Если имя ‘a.out’ кажется вам не очень выразительным, то вы наверняка захотите указать другое, делается это так:

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

Языковые стандарты

Для каждого языка программирования, который может компилироваться GCC и для которого существует стандарт, GCC пытается следовать одной или нескольким редакциям этого стандарта, возможно, с некоторыми исключениями и, возможно, с некоторыми расширениями. Явно указать, какого стандарта нужно придерживаться при компиляции вашей программы можно, используя ключ компиляции -std , присваивая ему значения, обозначающие тот или иной стандарт, например: скомпилирует нашу программу в режиме соответствия стандарту C++ 1998 года. Поскольку нас интересуют два языка, C и C++, посмотрим, какие из стандартов поддерживаются для них.

Язык C

GCC поддерживает три версии стандарта C, хотя последняя версия поддерживается лишь частично.

Первый стандарт C появился в 1989 как документ американской стандартизирующей организации ANSI и стал международным стандартом в следующем году, после ратицификации его международной организацией по стандартам ISO. Потребовать, чтобы GCC руководствовался именно им, можно, добавив один из трёх ключей компиляции: -ansi , -std=c89 или -std=iso9899:1990 .

В 1995 в язык была внесена первая «поправка» (amendment), которая добавила диграфы (комбинации символов, замещавших одиночные символы, которых могло не быть на некоторых клавиатурах), макрос __STDC_VERSION__ для того, чтобы в исходном коде можно было проверить версию стандарта, которую поддерживает компилятор, обрабатывающий этот код, и внёсшая изменения в стандартную библиотеку. Поддержка этой редакции включается ключом -std=iso9899:199409 .

Наконец, новая версия стандарта C была опубликована в 1999 и внесла довольно серьезные изменения в язык. Её GCC поддерживает пока не полностью, но эта поддержка включается ключом — std=iso9899:199409 .

По умолчанию GCC использует некоторые расширения языка C, которые довольно редко конфликтуют со стандартом. Использование ключей -std со значениями, перечисленными выше, заблокирует эти расширения. Потребовать явного использования расширений можно, указав ключи -std=gnu89 или -std=gnu99 для стандартов 1989-го и 1999-го годов, соответственно. По умолчанию компиляция программ C происходит в режиме -std=gnu89 , однажды он изменится на -std=gnu99 , когда поддержка стандарта 1999-го года станет полной. Кстати, некоторые расширения GCC для стандарта 1989-го года стали частью стандарта 1999.

Язык C++

GCC поддерживает стандарт ISO C++ (1998 год) и предоставляет экспериментальную поддержку грядущего нового стандарта ISO, который обычно обозначают как C++0x (имея ввиду предполагаемые сроки выхода: 200x). В стандарт 1998-го года вносилась поправка в 2003-м году. GCC реализует большую часть стандарта 1998 (ключевое слово export для раздельной трансляции шаблонов C++ является важным исключением) и почти целиком поправки 2003: -std=c++98 . Некоторые уже окончательно утверждённые средства C++0x можно использовать при использовании ключа -std=c++0x .

Как и в случае с языком C, по умолчанию GCC использует некоторые расширения языка C++, которые блокируются при указании одной из опций, описанных выше. Явное использование расширений можно затребовать, указав ключ -std=gnu++98 или -std=gnu++0x , первый используется по умолчанию при компиляции программ на C++.

Читайте также:  Ice 64 bit windows

Предупреждения компилятора

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

Примерами таких потенциально опасных мест могут служить:

  • операция = в условии оператора if , которую начинающие программисты на C или C++ часто путают с операцией == ;
  • неиспользуемые переменные (вы что-то хотели присвоить им, но забыли?);
  • отсутствие указанного каталога для поиска включаемых заголовочных файлов (вы опечатались в пути для этого каталога, а соответствующий заголовочный файл взят из какого-то другого места?)

Предупреждения компилятора это очень полезный механизм, позволивший программистам избежать многих незаметных на первый взгляд ошибок, которые могли проявиться только после запуска уже скомпилированной программы (возможно, много лет спустя после первого запуска). Его, безусловно, нужно использовать, и относиться к предупреждениям следует максимально внимательно. Многие авторитетные книги по C++, написанные в жанре сборников советов (Скотта Мейерса, Герба Саттера, Андрея Александреску, Стивена Дьюхерста), содержат совет компилировать программу на максимально строгом уровне предупреждений и добиваться того, чтобы компилятору не к чему было придраться.

Компилятор GCC позволяет включать несколько десятков видов предупреждений, для каждого из которых имеется свой ключ компиляции. Для удобства основные из них сгруппированы и включаются двумя ключами: -Wall и -Wextra . Перечислим, какие из предупреждений и соответствующих ключей оказываются задействованными при использовании этих двух и в каких случаях в результате программист будет получать предупреждения:

  • -Wall :
    • -Waddress
      подозрительное использование адресов в памяти; например, использование имени функции (которое вернёт её адрес) в условном выражении может означать, что программист хотел вызвать эту функцию и использовать её результат, но забыл поставить круглые скобки;
    • -Warray-bounds (только вместе с ключом -O2 , см. раздел ключи оптимизации)
      обращения к элементам массива, которые заведомо выводят за его пределы;
    • -Wc++0x-compat
      использование конструкций, значение которых изменится при переходе от стандарта ISO C++ 1998 к стандарту ISO C++ 200x; например, использование идентификаторов, которые станут ключевыми словами;
    • -Wchar-subscripts
      использование выражения типа char для обращения к элементу массива (внутри квадратных скобок); источником проблем является то, что на некоторых машинах char имеет знаковый тип, а на других беззнаковый;
    • -Wimplicit-int
      объявление не содержит типа: в C считается, что в этом случае использован int и это не приводит к синтаксической ошибке (зато часто приводит к более трудно уловимым); в C++ эта опция не нужна, так как «неявный int» запрещён и объявление без типа считается ошибкой;
    • -Wimplicit-function-declaration
      только для C: функция используется не будучи объявленной (в C++ это вызовет ошибку компиляции);
    • -Wcomment
      использование /* внутри уже начавшегося комментария /* (вложение комментариев не допускается и чаще всего приводит к ошибкам компиляции, но так случается не всегда);
    • -Wformat
      проверка соответствия форматных строк в функциях printf, scanf и других переданным в них аргументам;
    • -Wmain
      (только для C, в C++ возникнет ошибка компиляции) подозрительный тип функции main (в общем случае она должна быть функцией, возвращающей int, с внешней линковкой и имеющей 0, 2 или 3 параметра);
    • -Wmissing-braces
      нехватка фигурных скобок в инициализации массива, например: — на вторую строку будет выдано предупреждение;
    • -Wnonnull (только C)
      передача нулевого указателя на месте параметра, объявленного с ключевым словом nonnull;
    • -Wparentheses
      возможно, не хватает скобок (любого типа); например, предупреждение появится для выражения ‘x -Wpointer-sign (только C)
      передача указателей или присваивание с различной «знаковостью» (между знаковыми и беззнаковыми типами);
    • -Wreorder (только C++)
      порядок членов в списке инициализации конструктора класса или структуры не соответствует порядку их объявления;
    • -Wreturn-type
      объявление функции без возвращаемого типа, отсутствие return в не- void функции, наличие return у void функции; в C++ сообщение об отсутствующем return будет подано в любом случае, а отсутствие типа в объявлении считается ошибкой;
    • -Wsequence-point
      указывает на выражения, порядок вычисления которых не специфицирован стандартом и потому зависит от реализации (попросту говоря, непредсказуем), например: a = a++; , a[n] = b[n++]; ;
    • -Wsign-compare (только для C++)
      сравнение знаковых и беззнаковых выражений;
    • -Wstrict-aliasing (активно только если указан ключ оптимизации -fstrict-aliasing )
      преобразование типов небезопасное относительно оптимизаций компилятора, которые он выполняет на основе предположения о том, что любой участок памяти используется только для значений одного типа (или очень похожих типов);
    • -Wstrict-overflow=1 (активно только если указан ключ оптимизации -fstrict-overflow )
      предупреждает о возможных переполнениях типа в тех местах, где компилятор собирается выполнить оптимизацию, основанную на предположении об отсутствии переполнения;
    • -Wswitch
      при использовании переменной типа enum в операторе switch одно или несколько значений этого enum не перечислено в case (наличие default подавляет это предупреждение); метки case проверяющие значения за пределами данного enum также вызовут предупреждение;
    • -Wtrigraphs
      триграф используется вне комментария и может изменить смысл программы (триграф это последовательность символов, обозначающая некоторый специальный символ; задумывались для использования в тех странах, где клавиатуры не содержали необходимых для программирования символов, но сейчас абсолютно устарели);
    • -Wuninitialized (только при оптимизации с ключом -O1 или выше)
      переменная не инициализирована или может оказаться таковой в результате использования setjmp ;
    • -Wunknown-pragmas
      встречена директива компиляции #pragma с неизвестным GCC именем;
    • -Wunused-function
      статическая функция объявлена, но не определена, или невстраиваемая статическая функция определена, но не используется;
    • -Wunused-label
      метка объявлена но не использована;
    • -Wunused-value
      значение выражения, не имеющего сторонних эффектов, нигде не используется;
    • -Wunused-variable
      локальная переменная или статическая неконстантная переменная не используется нигде кроме своего объявления;
  • -Wextra (известна также как -W : это имя до сих пор поддерживается, но считается устаревшим и не рекомендуется к использованию в новых проектах):
    • -Wclobbered
      переменная может быть изменена вызовами longjmp или vfork ;
    • -Wempty-body
      пустое тело у if , else или do-while ; в C++ также будет выдано предупреждение для пустого тела циклов while и for , если перед ; , закрывающей цикл, не стоит пробельного символа (пустые <> не вызовут предупреждения);
    • -Wignored-qualifiers
      использован квалификатор типа (например, const ) для типа возвращаемого значения функции; в C++ это предупреждение возникнет только для скалярных встроенных типов и void в качестве возвращаемых типов;
    • -Wmissing-field-initializers
      пропущена инициализация одного из полей структуры;
    • -Wmissing-parameter-type (только C)
      пропущен тип параметра функции в её объявлении;
    • -Wold-style-declaration (только C)
      использован какой-либо стиль объявлений, признанный стандартом C устаревшим (например, static не на первом месте в объявлении);
    • -Woverride-init (только C)
      инициализация поля без сторонних эффектов перекрывается при последующем использовании «назначенных инициализаций» (designated initializers);
    • -Wsign-compare
      см. описание этого ключа в разделе -Wall ;
    • -Wtype-limits
      сравнение (обычно на неравенство) всегда имеет значение ‘истина’ (или всегда ‘ложь’) из-за ограничений типов; например, сравнение беззнаковой переменной с нулём;
    • -Wuninitialized
      см. описание этого ключа в разделе -Wall ;
    • -Wunused-parameter (только в присутствии -Wall или -Wunused )
      неиспользуемый параметр функции. -Wextra также выдаёт предупреждения в следующих случаях:
    • указатель сравнивается с нулём с помощью , , > или >= . Следующие опции имеют смысл только для C++:
    • члены enum используются в одном условном выражении вместе с не-членами этого enum ;
    • нестатическое поле-ссылка или нестатическое константное поле в классе без конструктора;
    • неоднозначность с виртуальными базовыми классами;
    • обращение к элементу массива, объявленному как register ;
    • взятие адреса переменной, объявленной как register ;
    • базовый класс не инициализирован в копирующем конструкторе класса-наследника.

После знакомства с концепцией предупреждений компилятора и поддерживаемыми стандартами языков, стоит заметить, что стандарты сами содержат набор рекомендаций к производителям компиляторов для генерации предупреждений. Включить соответствующие предупреждения можно указав для команды компиляции ключ -pedantic (это можно делать независимо от того, указаны ли другие ключи).

Далее в примерах при использовании GCC мы не будем указывать опции, включающие предупреждения, чтобы не загромождать код. Мы даём честное слово, что на практике всегда используем максимальный уровень предупреждений и рекомендуем вам делать то же.

Отладочная информация

Сложно представить себе программу, которая выполняет что-то осмысленное и которая была написана с первого раза без единой ошибки. Хуже того, иногда может оказаться, что давно и успешно работающая программа также содержит изъяны. Установить источник проблем помогает отладочная информация, которую компилятор может добавить в каждый объектный файл. Она включает в себя указание на типы данных, использованные в программе, и соответствие объектного кода строкам текста исходного файла. Отладочная информация, сгенерированная GCC, предполагается для использования в первую очередь отладчиком GDB (GNU debugger, отладчик GNU), но не только им.

Читайте также:  Отключить windows aero что это такое

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

  • stabs — используется отладчиком DBX на большинстве BSD-систем. На системах MIPS, Alpha и System V Release 4 эта опция даст результат, котрый не будет понят DBX или другим распространённым отладчиком SDB. На системе System V Release 4 потребует наличие GNU Assembler (ассемблера GNU).
  • COFF — используется отладчиком SDB на большинстве System V, до версии System V Release 4.
  • XCOFF — используется отладчиком DBX на системах IBM RS/6000.
  • DWARF 2 — используется DBX на IRIX 6.
  • VMS — используется программой DEBUG на системах VMS.

Каждый из перечисленных форматов может быть использован, только если он поддерживается текущей платформой. Указать формат можно с помощью ключа -gимя_формата_строчными_буквами . Для форматов stabs и COFF можно добавить к ключу символ ‘+’ справа без пробела, это включит дополнительную информацию, специфичную для GDB и может помешать работе других отладчиков с этим файлом.

Указав ключ -g , можно получить информацию в формате, родном для текущей архитектуры (но будут использоваться расширения, специфичные для GDB). Самая подробная с точки зрения GDB отладочная информация, доступная на данной платформе, будет сгенерирована при использовании ключа -ggdb (будет использован максимально выразительный доступный на данной платформе формат).

К каждому из упомянутых ключей можно дописать справа цифру от 0 до 3, она будет обозначать уровень отладочной информации, иначе говоря — её объем:

  • Уровень 0 отключает генерацию отладочной информации.
  • Уровень 1 обеспечивает минимум отладочной информации, позволяющей просматривать стеки вызовов подпрограмм, которые вы не планировали отлаживать. Включаются описания функций и внешних переменных, но отсутствует информация о локальных переменных и номерах строк исходного кода.
  • Уровень 2 используется по умолчанию и добавляет к уровню 1 информацию о локальных переменных и номерах строк исходного кода.
  • Уровень 3 включает дополнительную информацию, например, определения макросов.

В заключение этого пункта следует сказать ещё две вещи. Во-первых, как и многие современные компиляторы, GCC поддерживает разные уровни оптимизации генерируемого кода, они задаются ключом -O и сопутствующими. Некоторые современные компиляторы в то же время не позволяют использовать одновременно отладочную информацию и оптимизацию, другими словами, ключи семейств -g и -O конфликтуют. Последнее не относится к GCC: он позволяет совмещать указанные возможности. При этом отладка может выявить интересные эффекты оптимизации: например, строки кода исполняются в неожиданном порядке, некоторые из них просто пропускаются, некоторые объявленные переменные могут исчезнуть (не проявляться при отладке).

Во-вторых, отладочная информация все же занимает дополнительное пространство в памяти (темпы удешевления которой, впрочем, настолько высоки, что вряд ли можно оправдать пренебрежительное отношение к генерации отладочной информации), которое вы можете захотеть сэкономить. Для этого существует ключ -s (от strip — удалить). Размер файлов с отладочной информацией и без неё может отличаться довольно сильно, особенно на маленьких программах. Сравним размеры исполняемых файлов нашей первой программы в трёх случаях:

  1. C ключом -s .
  2. Без каких-либо ключей, касающихся отладки.
  3. С ключом -ggddb3 , который должен поместить довольно много отладочной информации в исполняемый файл.

Оптимизация

Если с предупреждениями компилятора всё ясно (действует принцип «чем больше, тем лучше»), количество отладочной информации также, в основном, контролируется программистом в соответствии с его целями (доведение программы до работоспособного состояния или изготовление окончательной версии программы для передачи заказчику), то с оптимизацией дело обстоит намного сложней. Оптимизация это процесс улучшения производительности программы, в том числе, увеличение скорости её работы и снижение объёма занимаемой памяти (эти две цели, между прочим, иногда противоречат друг другу). GCC поддерживает большой набор ключей оптимизации и часть из них объединены в группы (как и в случае с предупреждениями), что позволяет активировать их также по групповому принципу.

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

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

Если компилятор не получает каких-либо флагов оптимизации, его основная цель — снизить время компиляции (которое при сборках больших программ на C или C++ может достигать внушительных показателей и, тем самым, сильно тормозить процесс разработки) и обеспечить предсказуемость и корректность процедуры отладки (если она будет производиться в будущем). Групповые флаги оптимизации имеют вид: -Oуровень , где уровень может изменяться от 0 до 3 (каждый следующий включает в себя все оптимизации предыдущего).

  • -O1 (также носит обозначение -O , признанное устаревшим)
    Мягкая оптимизация, некоторое увеличение времени компиляции, возможно ощутимое увеличение памяти для больших функций; включены оптимизации, которые должны одновременно уменьшать общую занимаемую память и ускорять код.
  • -02
    Включает практически все доступные оптимизации, не ухудшающие сильно один из двух показателей двоичного кода — память или скорость — за счёт другого; не включает развертку циклов и автоматическое встраивание функций.
  • -O3
    Наиболее «агрессивная» оптимизация, включаются развертку циклов и автоматическое встраивание функций.
  • -O0
    Отключение оптимизаций, основная цель: высокая скорость компиляции и предсказуемость результатов отладки; эта опция задействована по умолчанию.
  • -Os
    Оптимизация размера программы. Включаются те опции из набора -O2 , которые обычно не увеличивают объём кода. Также применяются некоторые другие оптимизации, направленные на снижение его объёма.

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

Раздельная трансляция

Реальные программы сильно превышают размеры нашего первого примера ‘first.cpp’ по количеству строк кода. Когда оно увеличивается достаточно сильно, появляются проблемы:

  • любое изменение в тексте файла с программой приводит к его полной перекомпиляции, что занимает ощутимое время;
  • сложно организовать коллективную разработку (согласовать внесение изменений в один файл несколькими программистами);
  • затрудняется навигация по созданному коду.

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

Начнём с простого случая, когда программа представляет из себя набор функций, и мы хотим вынести их часть в отдельный ‘.cpp’-файл. Возьмём уже использовавшийся пример с программой ‘first.cpp’ и добавим в неё вызов функции, определение которой помещается в файле ‘second.cpp‘: Обратите внимание на объявление int f(); в ‘first.cpp’: правила языка C++ требуют объявить каждую функцию перед первым использованием. Обычно вместо того, чтобы вручную писать объявления всех функций, определённых в другой единице трансляции (‘.cpp’-файле), используют заголовочные файлы, содержащие все необходимые объявления, текст которых целиком включается в нужный ‘.cpp’-файл директивой препроцессора C #include . Обычно для каждого ‘.cpp’-файла, который не содержит точки входа в программу (функции main ), а значит, содержимое которого будет использовано другими частями программы, создаётся заголовочный файл с таким же основным именем, но другим расширением. В нашем примере можно добавить заголовочный файл ‘second.hpp’ и получить эквивалентный приведённому выше, но в перспективе более удобный в использовании код: Директивы препроцессора C (строки, начинающиеся # ) в заголовочном файле — «стражи включения», — как обычно, добавляются для того, чтобы данный файл не был по случайности включён в один ‘.cpp’-файл несколько раз (символ SECOND_HPP должен быть своим у каждого заголовочного файла, потому разумно связывать его с именем этого файла).

Вернёмся от увлекательных особенностей C++ к использованию GCC. Оба указанных выше варианта компилируются и линкуются в один исполняемый файл одинаково, при помощи простого перечисления: Стоит отметить удобную возможность использования шаблонов имён (wildcards), чтобы избежать полного перечисления файлов: даст аналогичный приведённому выше результат.

Итак, мы сумели разнести код в несколько файлов, но пока еще не добились того, чтобы изменения в одном файле не приводили к перекомпиляции всех. Здесь снова придется вспомнить о том, что используемые до этого момента вызовы gcc ( g++ ) приводят не только к компиляции, но и к линковке полученных после компиляции объектных фалов в один исполняемый файл. Нам потребуется явно разбить создание исполняемого файла на две стадии, используя ключ gcc -с , который просит оставить получившиеся объектные файлы на диске и не линковать их: Как видно, на диске появились два объектных файла (расширение ‘.o’), которые, очевидно, соответствуют исходным ‘.cpp’. Слинковать и получить исполняемый файл можно обычным образом: (GCC определяет тип файлов и не пытается компилировать их как исходные тексты, а сразу линкует). Теперь, внеся изменение в файл ‘second.cpp’, можно перекомпилировать только его и заново слинковать все объектные файлы программы: Не поленитесь исправить в тексте ‘second.cpp’ 42 на ваше любимое число и убедиться, что эта схема работает.

Читайте также:  Viva pinata для windows

Библиотеки

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

Библиотеки могут существовать в двух различных вариантах:

  • статические: код из библиотеки добавляется в исполняемый файл на стадии линковки, и после её окончания файл библиотеки больше не нужен полученной программе;
  • динамические: код из библиотеки не добавляется в исполняемый файл, а загружается в память во время запуска программы, таким образом, он должен быть доступен при каждом запуске;

Бесспорное преимущество динамических библиотек состоит в том, что если несколько программ используют одну библиотеку, то она загружается в память только один раз. Иными словами, сразу несколько программ могут (и будут) использовать один загруженный экземпляр библиотеки «одновременно». В то же время использование статической библиотеки заставит добавлять части её кода в каждый исполняемый файл по отдельности. Обновление динамической библиотеки потребует перезапуска, использующих её программ, статической — их перелинковки (что обычно занимает немало времени).

Статические библиотеки

Рассмотрим сначала более простой случай статической библиотеки. Для создания библиотеки такого типа из набора объектных файлов используется утилита ar . Добавим к двум имеющимся ‘.cpp’-файлам третий, ‘third.cpp’ (с произвольным содержимым), скомпилируем его в объектный файл и сделаем из двух объектных (‘second.o’, ‘third.o’) статическую библиотеку: Опции ar означают следующее:

  • c — создать (create) библиотеку,
  • r — заменить (replace) совпадающие объектные файлы внутри библиотеки указанными новыми (в случае, если библиотека существовала),
  • s — создать индекс внутри библиотеки (может быть, у вас получится догадаться о происхождении имени ключа. ).

Важным является формат имени файла библиотеки:

  • префикс ‘lib’,
  • расширение ‘.a’.

Для указания GCC на библиотеки, которые нужно использовать при сборке, понадобятся ещё два ключа:

  • l — для имени библиотеки,
  • L — для указания пути к ней.

Компиляция нашей программы с использованием созданной библиотеки может выглядеть так: С помощью -L. текущий каталог добавляется в список каталогов для поиска библиотек (чтобы добавить несколько каталогов, нужно использовать ключ -L несколько раз). Обратите внимание, что именем библиотеки считается подстрока имени ‘.a’-файла между ‘lib’ слева и ‘.a’ справа. Здесь, в отличие от рассмотренных ранее случаев, порядок аргументов g++ важен, поскольку -L и -l это опции линковщика, который вступает в работу после компилятора, обрабатывающего первый аргумент (имя файла).

Динамические библиотеки

Создание и использование динамической библиотеки более сложны. При компиляции исходных файлов в объектные, которые станут основой для будущей библиотеки, нужно указать один дополнительный ключ: (мы не упоминаем файл ‘third.cpp’, чтобы не загромождать год: создадим библиотеку из одного файла). Ключ -fPIC позволяет создавать независимый от размещения в оперативной памяти код (position independent code), что позволяет использовать один библиотечный код из разных программ (находящихся, очевидно, в разных местах оперативной памяти) во время их выполнения. Будем для краткости называть результирующий файл этой команды так: объектный pic-файл. Можно вместо ключа -fPIC использовать -fpic аналогичного назначения, который может создать файл меньшего размера, но работает не на всех платформах. Если на вашей платформе он не поддерживается, GCC сообщит вам об этом.

Файлы динамических библиотек должны, как и раньше, иметь префикс ‘lib’ и расширение ‘.so’ (shared object). Сложность состоит в том, что для одной библиотеки надо иметь в виду четыре имени, причем три из них соответствуют объектам файловой системы:

    «Реальное имя» (real name): имя файла на диске, содержащего код библиотеки. Должно иметь форму:

Рассмотрим команду получения файла библиотеки (который должен носить «реальное имя», п. 1 списка выше) из объектного pic-файла: Ключ -shared имеет вполне понятное значение: мы сигнализируем GCC о том, что хотим получить динамическую библиотеку («разделяемый объект»). После ключа -o идёт имя выходного файла («реальное имя» файла нашей библиотеки), а затем список файлов, которые должны в эту библиотеку войти: в нашем случае это всего один объектный файл ‘second.cpp’.

Теперь попробуем разобраться с тем, что написано между ключами -shared и -o . Ключ -Wl говорит о том, что далее пойдут опции, которые предназначаются непосредственно линковщику. В соответствии с синтаксическими правилами, в списке этих опций не должно присутствовать пробелов: мы передаём GCC что-то, на что он должен «закрыть глаза» (опции предназначены не ему, а линковщику) и в этом случае мы договариваемся о том, что он откроет глаза только когда встретит первый пробел. В качестве символа-разделителя в списке этих опций вместо пробела выступает запятая. Теперь должно быть ясно, что между ключами -shared и -o стоит опция, передаваемая линковщику, которая указывает на «so-имя» (п. 2 списка выше) создаваемой библиотеки.

Такое усложнение возникает из основного принципа работы GCC (как и большинства Unix-программ): решаемая задача неявно разбивается на подзадачи, которые выполняют отдельные утилиты. Это создаёт некоторый уровень абстракции: для большинства операций мы просто вызываем команду g++ и не задумываемся, какие именно программы трудятся над обработкой наших файлов, а зачастую их (программ) бывает немало. В случае с -Wl мы натолкнулись на явление, которое Джоэль Спольски назвал «протекающими абстракциями» (его статью «Закон дырявых абстракций» на эту тему можно и сейчас найти в интернете). Более прозрачное решение текущей задачи (получение библиотечного файла из объектного(ых) pic-файла(ов)) состоит в прямом вызове линковщика, минуя g++ :Результат в данном случае будет аналогичным.

Обсудим теперь процесс загрузки библиотек в память. Он, как было сказано, происходит при запуске первой программы, которая использует данную динамическую библиотеку. Загрузка необходимых библиотек при запуске программы осуществляется специальной программой, являющейся частью операционной системы — динамическим линковщиком. Он ищет файлы библиотек в некоторых заранее определённых каталогах, список которых на многих Unix-подобных операционных системах хранится в файле /etc/ld.so.conf . Почти наверняка в этом списке имеются каталоги /lib и /usr/lib . Возможно, сюда входит и /usr/local/lib . Как это часто бывает, разные авторитетные организации и специалисты советуют использовать для динамических библиотек разные папки. Мы остановим свой выбор на /usr/lib . Создание необходимой инфраструктуры библиотечных файлов: одного реального, ссылки с so-именем и ссылки с именем для линковщика — в каком-то из системных каталогов для хранения библиотек часто называют установкой библиотеки.

Теперь, наконец, посмотрим на весь путь от создания динамической библиотеки до запуска программы, её использующей: Приятной неожиданностью можно считать то, что сборка основной программы (из файла ‘first.cpp’) осталась практически без изменений по сравнению со случаем статической библиотеки (за исключением того, что исчезла необходимость в указании пути для поиска файла библиотеки с ключом -L : это и понятно, все библиотечный файлы находятся теперь в системных каталогах). Выполняя эту операцию, мы использовали четвёртое имя библиотеки (п. 4 списка выше).

Обращает на себя внимание необходимость наличия администраторских прав (вызов sudo ) для установки библиотеки. Во время тестовых запусков программы можно обойтись без копирования в системный каталог /usr/lib/ , если использовать переменную окружения LD_LIBRARY_PATH , которая содержит пути для поиска библиотек. В этом случае перед запуском программы её нужно модифицировать, а также снова (как в случае со статической библиотекой) указать ключ -L :

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