Учебные программы » Системы реального времени » Дополнительные материалы » Р. Столлман. gcc: компилятор языков C, C++, Objective C (часть 1)
Ричард Столлман. gcc: компилятор языков C, C++, Objective C (часть 1)
gcc: Компилятор языков C, C++, Objective C (часть 1) ==================================================================== Руководство пользователя gcc 2.7 Ричард Столлман (Richard Stallman) - 2 - Содержание 1. Компиляция C, C++ или Objective C 4 2. Командные Опции GNU CC 6 2.1. Сводка Опций 7 2.2. Опции, Управляющие Видом Вывода 9 2.3. Опции, Управляющие Диалектом C 12 2.4. Опции для Включения или Подавления Предупреждений 17 2.5. Опции для Отладки Ваших Программ или GNU CC 27 2.6. Опции, которые Управляют Оптимизацией 34 2.7. Опции Управляющие Препроцессором 40 2.8. Передача Опций Ассемблеру 44 2.9. Опции Линковки 44 2.10. Опции для Поиска в Директориях 48 2.11. Указание Целевой Машины и Версии Компилятора 49 2.12. Модели и Конфигурации Машин 51 2.12.1. Опции Intel 386 52 2.13. Опции Соглашений о Генерации Кода 55 2.14. Переменные Окружения, Затрагивающие GNU CC 60 2.15. Выполнение Protoize 62 3. Установка GNU CC 67 3.1. Конфигурации Поддерживаемые GNU CC 76 3.2. Компиляция в Отдельном Каталоге 78 3.3. Построение и Установка Кросскомпилятора 79 3.3.1. Шаги Кросскомпиляции 80 3.3.2. Конфигурирование Кросскомпилятора 80 3.3.3. Инструментальные Средства и Библиотеки для Кросскомпилятор 81 3.3.4. Реальное Построение Кросскомпилятора 82 3.4. Стандартные Директории Заголовочных Файлов 83 4. Расширения Семейства Языка C 84 4.1. Операторы и Объявления в Выражениях 84 4.2. Локально Объявляемые Метки 85 4.3. Метки как Значения 87 4.4. Вложенные Функции 88 4.5. Конструирование Вызовов Функций 91 4.6. Именование Типа Выражения 93 4.7. Ссылки на Тип с Помощью typeof 93 4.8. Обобщенные L-значения 94 - 3 - 4.9. Условные Выражения с Опущенными Операндами 95 4.10. Двухсловные Целые 96 4.11. Комплексные Числа 96 4.12. Массивы Нулевой Длины 97 4.13. Массивы Переменной Длины 97 4.14. Макросы с Переменным Числом Аргументов 99 4.15. Массивы Не L-значения Могут Иметь Индексы 100 4.16. Арифметика над Указателями на void и на Функции 101 4.17. Неконстантные Инициализаторы 101 4.18. Выражения Конструкторов 101 4.19. Помеченные Элементы в Инициализаторах 102 4.20. Диапазоны Case 104 4.21. Приведение к Типу Объединения 104 4.22. Объявления Атрибутов Функций 105 4.23. Прототипы и Определения Функций в Старом Стиле 109 4.24. Комментарии в C++ Стиле 109 4.25. Знак Доллара в Идентификаторах 110 4.26. Символ ESC в Константах 110 4.27. Выравнивание Типов и Переменных 110 4.28. Указание Атрибутов Переменных 111 4.29. Указание Атрибутов Типов 114 . - 4 - 1. Компиляция C, C++ или Objective C C, C++ и Objective C версии компилятора объединены; компилятор GNU C может компилировать программы написанные на C, C++ или Objective C. "GCC" - общее стандартное обозначение для компилятора GNU C. Это как наиболее общее название компилятора, так и название, используемое, когда акцент делается на компиляции C программ. Когда ссылаются на C++ компиляцию, обычно называют компилятор "G++". Поскольку есть только один компилятор, будет точным называть его "GCC" вне зависимости от языка; однако термин "G++" более полезен, когда ударение стоит на компиляции С++ программ. Мы используем имя "GNU CC" для ссылки на всю систему компиляции в целом и более конкретно к языковонезависимой части компилятора. Например, мы говорим об опциях оптимизации, как о влияющих на поведение "GNU CC" или, иногда, просто "компилятора". Внешние интерфейсы с других языков, таких как Ada 9X, Fortran, Modula-3 и Pascal, находятся в развитии. Эти front end'ы, также как и front end с C++, построенны в поддиректориях GNU CC и связанны с ним. В результате получается интегрированный компилятор, который может компилировать программы написанные на C, C++, Objective C или на любых других языках, для которых вы установили внешние интерфейсы. В данном руководстве мы рассматриваем только опции для C, Objective C и C++ компиляторов, а также опции ядра GNU CC. Обращайтесь к документации по другим внешним интерфейсам, чтобы узнать об опциях, используемых при компиляции программ, написанных на других языках. G++ - это компилятор, а не просто препроцессор. G++ строит объектный код прямо из вашей исходной C++ программы. Никакой - 5 - промежуточной C версии программы не порождается. (К примеру, некоторые другие реализации напротив используют программу, которая порождает C программу из вашей C++ программы.) Избегание промежуточного C представления программы означает, что вы получаете более хороший объектный код и более хорошую отладочную информацию. Отладчик GNU, GDB, использует эту информацию в объектном коде, чтобы дать вам все возможности работы на уровне исходного C++ текста (см. раздел "C и C++" в "Отладка с GDB"). . - 6 - 2. Командные Опции GNU CC Когда вы вызываете GNU CC, он обычно выполняет препроцессирование, компиляцию, ассемблирование и линковку. "Общие опции" позволяют вам остановить этот процесс на промежуточной стадии. Например, опция '-c' говорит не запускать линкер. Тогда вывод состоит их объектных файлов, порожденных ассемблером. Другие опции передаются на одну из стадий обработки. Одни опции управляют препроцессором, другие самим компилятором. Все еще имеются опции, управляющие ассемблером и линкером; большинство из них не документировано здесь, поскольку вам редко требуется использовать какую-нибудь из них. Большая часть опций командной строки, которые вы можете использовать с GNU CC полезны для C программ; если опция полезна только для других языков (обычно C++), в объяснениии сказано об этом прямо. Если в описании какой-либо опции не упоминается исходный язык, вы можете использовать эту опцию со всеми поддерживаемыми языками. См. раздел [Компиляция C++ программ], чтобы найти сводку опций для компиляции C++ программ. Программа gcc принимает опции и имена файлов как операнды. Многие опции имеют многобуквенные имена; следовательно, многочисленные однобуквенные опции не могут быть сгруппированны: '-dr' очень отличается от '-d -r'. Вы можете смешивать опции и другие аргументы. По большей части, используемый порядок не имеет значения. Порядок важен, когда вы используете несколько опций одного вида; например, если вы указываете '-L' больше чем один раз, директории просматриваются в порядке указания. Многие опции имеют длинные имена, начинающиеся с '-f' или с - 7 - '-W' - например, '-fforce-mem', '-fstrength-reduce', '-Wformat' и так далее. Большинство из них имеет положительную и отрицательную формы; отрицательной формой '-ffoo' будет '-fno-foo'. Это руководство документирует только одну из этих форм - ту, которая не принимается по умолчанию. 2.1. Сводка Опций Здесь изложена сводка всех опций, сгруппированная по типу. Пояснения расположены в следующих разделах. Общие Опции См. раздел [Опции, Управляющие Видом Вывода]. -c -S -E -o FILE -pipe -v -язык Опции языка C См. раздел [Опции, Управляющие Диалектом C]. -ansi -fallow-single-precision -fcond-mismatch -fno-asm -fno-builtin -fsigned-bitfields -fsigned-char -funsigned-bitfields -funsigned-char -fwritable-strings -traditional -traditional-cpp -trigraphs Опции Предупреждений См. раздел [Опции для Включения или Подавления Предупреждений]. -fsyntax-only -pedantic -pedantic-errors -w -W -Wall -Waggregate-return -Wbad-function-cast -Wcast-align -Wcast-qual -Wchar-subscript -Wcomment -Wconversion -Wenum-clash -Werror -Wformat -Wid-clash-LEN -Wimplicit -Wimport -Winline -Wlarger-than-LEN -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wno-import -Woverloaded-virtual -Wparentheses -Wpointer-arith -Wredundant-decls -Wreorder -Wreturn-type -Wshadow -Wstrict-prototypes -Wswitch -Wsynth -Wtemplate-debugging -Wtraditional -Wtrigraphs -Wuninitialized -Wunused -Wwrite-strings Опции Отладки - 8 - См. раздел [Опции для Отладки Вашей Программы или GNU CC]. -a -dбуквы -fpretend-float -g -gуровень -gcoff -gdwarf -gdwarf+ -ggdb -gstabs -gstabs+ -gxcoff -gxcoff+ -p -pg -print-file-name=библиотека -print-libgcc-file-name -print-prog-name=программа -print-search-dirs -save-temps Опции Оптимизации См. раздел [Опции, которые Управляют Оптимизацией]. -fcaller-saves -fcse-follow-jumps -fcse-skip-blocks -fdelayed-branch -fexpensive-optimizations -ffast-math -ffloat-store -fforce-addr -fforce-mem -finline-functions -fkeep-inline-functions -fno-default-inline -fno-defer-pop -fno-function-cse -fno-inline -fno-peephole -fomit-frame-pointer -frerun-cse-after-loop -fschedule-insns -fschedule-insns2 -fstrength-reduce -fthread-jumps -funroll-all-loops -funroll-loops -O -O0 -O1 -O2 -O3 Опции Препроцессора См. раздел [Опции, Управляющие Препроцессором]. -Aвопрос(ответ) -C -dD -dM -dN -Dмакрос[=значение] -E -H -idirafter директорий -include файл -imacros файл -iprefix файл -iwithprefix директорий -iwithprefixbefore директорий -isystem директорий -M -MD -MM -MMD -MG -nostdinc -P -trigraphs -undef -Uмакрос -Wp,опция Опции Ассемблера См. раздел [Передача Опции Ассемблеру]. -Wa,опция Опции Линкера См. раздел [Опции Линковки]. имя-объектного-файла -lбиблиотека - 9 - -nostartfiles -nodefaultlibs -nostdlib -s -static -shared -symbolic -Wl,опция -Xlinker опция -u символ Опции Директориев См. раздел [Опции для Поиска в Директориях]. -Bпрефикс -Iдиректорий -I- -Lдиректорий Целевые Опции См. раздел [Указание Целевой Машины и Версии Компилятора]. -b машина -V версия Машинозависимые Опции См. раздел [Модели и Конфигурации Машин]. Опции i386 -m486 -m386 -mieee-fp -mno-fancy-math-387 -mno-fp-ret-in-387 -msoft-float -msvr3-shlib -mno-wide-multiply -mrtd -malign-double -mreg-alloc=список -mregparm=число -malign-jumps=число -malign-loops=число -malign-functions=число Опции Генерации Кода См. раздел [Опции Соглашений о Генерации Кода]. -fcall-saved-регистр -fcall-used-регистр -ffixed-регистр -finhibit-size-directive -fno-common -fno-ident -fno-gnu-linker -fpcc-struct-return -fpic -fPIC -freg-struct-return -fshared-data -fshort-enums -fshort-double -fvolatile -fvolatile-global -fverbose-asm -fpack-struct +e0 +e1 2.2. Опции, Управляющие Видом Вывода Компиляция может включать до четырех стадий: препроцессирование, собственно компиляцию, ассемблирование и линковку, всегда в этом порядке. Первые три стадии применяются к отдельному исходному файлу и заканчиваются получением объектного файла; линковка объединяет все объектные файлы (заново откомпилированные или полученные как входные) в исполняемый файл. Для любого имени входного файла суффикс определяет какая компиляция требуется: file.c Исходный код на C, который нуждается в препроцессировании. file.i Исходный код на С, который не нуждается в препроцессировании. file.ii Исходный код на C++, который не нуждается в препроцессировании. file.m Исходный код на Objective C. Заметим, что вам необходимо подключить библиотеку 'libobjc.a', чтобы заставить Objective C программу работать. file.h C заголовочный файл (не для компиляции или линковки). file.cc file.cxx file.cpp file.C Исходный код на C+, который нуждается в препроцессировании. file.s Ассемблерный код. file.S Ассемблерный код, который нуждается в препроцессировании. другие Объектный файл, который нужно отдать прямо на линковку. Так поступают с любым именем файла с нераспознанным суффиксом. Вы можете прямо указать входной язык при помощи опции '-x': -x язык. Прямо специфицирует язык последующих входных файлов (даже если компилятор может выбрать язык на основании суффикса имени файла). Эта опция действует на все входные файлы вплоть до следующего появления опции '-x'. Возможными значениями для языка являются: c objective-c c++ c-header cpp-output c++-cpp-output assembler assembler-with-cpp -x none Выключает любое указание языка так, что последующие файлы обрабатываются в соответствии с суффиксами имен файлов (как если бы '-x' не указывалось бы вовсе). Если вам нужны лишь некоторые из стадий компиляции, вы можете использовать '-x', чтобы указать gcc где начать, и одну из опций '-c', '-S' или '-E', чтобы указать, где gcc должен остановиться. Заметим, что некоторые комбинации (например, '-x cpp-output -E' указывают gcc ни делать вообще ничего). -c Компилировать или ассемблировать исходные файлы, но не линковать. Стадия ликовки просто не выполняется. Конечный вывод происходит в форме объектного файла для каждого исходного файла. По умолчанию, имя объектного файла делается из имени исходного файла заменой суффикса '.c', '.i', '.s', и.т.д. на '.o'. Нераспознанные входные файлы, не требующие компиляции или ассемблирования, игнорируются. -S Остановиться после собственно компиляции; не ассемблировать. Вывод производится в форме файла с ассемблерным кодом для каждого не ассемблерного входного файла. По умолчанию, имя файла с ассемблерным кодом делается из имени исходного файла заменой суффикса '.c', '.i', и.т.д. на '.s'. Входные файлы, которые не требуют компиляции игнорируются. -E Остановиться после стадии препроцессирования; не запускать собственно компилятор. Вывод делается в форме препроцессированного исходного кода, который посылается на стандартный вывод. Входные файлы, которые не требуют препроцессирования игнорируются. -o файл Поместить вывод в файл 'файл'. Эта опция применяется вне зависимости от вида порождаемого файла, есть ли это выполнимый файл, объектный файл, ассемблерный файл или препроцессированный C код. Поскольку указывается только один выходной файл, нет смысла использовать '-o' при компиляции более чем одного входного файла, если вы не порождаете на выходе выполнимый файл. Если '-o' не указано, по умолчанию выполнимый файл помещается в 'a.out', объектный файл для 'исходный.суффикс' - в 'исходный.o', его ассемблерный код в 'исходный.s' и все препроцессированные C файлы - в стандартный вывод. -v Печатать (в стандартный вывод ошибок) команды выполняемые для запуска стадий компиляции. Также печатать номер версии управляющей программы компилятора, препроцессора и самого компилятора. -pipe Использовать каналы вместо временных файлов для коммуникации между различными стадиями компиляции. Это может не работать на некоторых системах, где ассемблер не может читать из канала, но ассемблер GNU не имеет проблем. 2.3. Опции, Управляющие Диалектом C Следующие опции управляют диалектами C (или или языков порожденных из C, таких как C++ и Objective C), которые воспринимает компилятор: -ansi Поддерживает все ANSI C программы. Эта опция выключает некоторые свойства GNU CC, которые несовместимы с ANSI C такие, как ключевые слова asm, inline и typeof, предопределенные макросы такие, как unix и vax, которые идентифицируют тип используемой вами системы. Она также включает нежелательные и редко используемые ANSI трехсимвольные последовательности, не разрешает '$' в идентификаторах и комментарии в стиле C++ '//'. Альтернативные ключевые слова __asm__, __extension__, __inline__ и __typeof__ продолжают работать не смотря на '-ansi'. Вы, разумеется, не хотели бы использовать их в ANSI C программе, но полезно использовать их в заголовочных файлах, которые могут быть включены в компиляции с '-ansi'. Опция '-ansi' не действует так, что не ANSI C программы беспричинно отбрасываются. Чтобы это было так, в добавление к '-ansi' требуется опция '-pedantic'. См. раздел [Опции Предупреждений], стр. 35. Когда используется опция '-ansi', предопределен макрос __STRICT_ANSI__. Некоторые заголовочные файлы могут заметить этот макрос и воздерживаться от объявления определенных функций или определения определенных макросов, к которым ANSI стандарт не обращается; это делается, чтобы избежать помех программам, которые могут использовать эти имена для других целей. Функции alloca, abort, exit и _exit не являются встроенными функциями при использовании '-ansi'. -fno-asm Не распознаются как ключевые слова asm, inline и typeof, и они могут быть использованы в коде в качестве идентификаторов. Вы можете использовать вместо них ключевые слова __asm__, __inline__ и __typeof__. '-ansi' включает '-fno-asm'. -fno-builtin Не распознаются встроенные функции кроме тех, имена которых начинаются с двух подчеркиваний. На данный момент затрагиваемыми функциями являются abort, abs, alloca, cos, exit, fabs, ffs, labs, memcmp, memcpy, sin, sqrt, strcmp, strcpy и strlen. GCC обычно генерирует специальный код, чтобы более эффективно обрабатывать некоторые встроенные функции; например, вызов alloca может стать просто последовательностью инструкций, которые прямо выравнивают стек, а вызов memcpy может превратиться просто в цикл копирования. Результирующий код является и более коротким, и более быстрым, но поскольку вызова функции как такового больше не существует, вы не можете ни поставить на него точку останова, ни изменить поведение функции прилинковав другую библиотеку. - 14 - Опция '-ansi' не дает функциям alloca и ffs быть встроенными, поскольку эти функции не имеют предопределенного значения в стандарте ANSI C. -trigraphs Поддерживаются трехсимвольные последовательности ANSI C. Опция '-ansi' включает '-trigraphs'. Не забивайте себе голову этим сумасшедствием. -traditional Пытается поддержать некоторые свойства традиционных компиляторов C. А именно: * Все extern объявления имеют глобальное действие, даже если они написаны внутри определения функции. Это распространяется и на точные объявления функций. * Более новые ключевые слова typeof, inline, signed, const и volatile не распознаются. (Вы все еще можете использовать альтернативные ключевые слова такие, как __typeof__, __inline__ и т.д.) * Всегда разрешены сравнения между целыми и указателями. * Целые типы unsigned short и unsigned char расширяются к unsigned int. * Константы с плавающей точкой вне диапозона не являются ошибками. * Некоторые конструкции, которые ANSI считает одним неправильным числом, такие как '0xe-0xd', считаются выражениями. * Строковые "константы" не обязательно константны; они размещаются в памяти доступной для записи, и одинаково выглядящие константы размещаются в разных местах. (Это тот же действие, что и у '-fwritable-strings'.) * Все автоматические переменные не объявленные register - 15 - сохраняются при longjump'е. Обычно GNU C следует ANSI C: автоматические переменные не объявленные volatile могут затираться. * Символьные последовательности '\x' и '\a' воспринимаются как 'x' и 'a' соответственно. Без '-traditional', '\x' является префиксом для шестнадцатеричного представления символа, а '\a' порождает символ звонка. Вы можете захотеть использовать 'fno-builtin' так же как и '-traditional', если ваша программа использует имена, которые обычно являются именами встроенных функций GNU C для его собственных целей. Вы не можете использовать '-traditional', если вы включаете какие либо заголовочные файлы, которые полагаются на особенности ANSI C. Некоторые продавцы поставляют системы с ANSI C заголовочными файлами и вы не можете использовать '-traditional' на таких машинах для компиляции файлов, который включают системные заголовочные файлы. В препроцессоре коментарии преобразуются в ничто, а не в пробел. Это разрешает традиционное объединение лексем. В директиве препроцессора символ '#' должен быть первым в строке. В препроцессоре аргументы макроса распознаются в строковых константах в определении макроса (их значения превращаются в строки, но без дополнительных кавычек, когда они появляются в соответствующем контексте). Предопределенный макрос __STDC__ не определен, если вы используете '-traditional', но __GNUC__ определен (поскольку расширения GNU, о которых говорит __GNUC__ не затрагиваются опцией '-traditional'). Если вам нужно написать заголовочный файл, который работает по-разному в зависимости от того, используется ли опция '-traditional', проверив эти два макроса вы можете различить четыре ситуации: GNU C, традиционный GNU C, другие ANSI C компиляторы и другие компиляторы старого C. Предопределенный макрос __STDC_VERSION__ также не определен, когда вы используете '-traditional'. См. раздел - 16 - "Стандартные Предопределенные Макросы" в 'Препроцессоре C' по поводу этих и других предопределенных макросов. Препроцессор считает, что строковые константы всегда заканчиваются при переходе на новую строку (если только ему не предшествует '\'). (Без '-traditional' в строковых константах может быть символ новой строки так, как набрано.) -traditional-cpp Пытается поддержать некоторые особенности традиционных препроцессоров C. Это включает в себя пять пунктов в таблице непосредственно выше, но без других эффектов '-traditional'.) -fcond-mismatch Позволяются условные выражения с не согласоваными типами второго и третьего аргументов. Значение такого выражения есть void. -funsigned-char Тип char считается беззнаковым, как unsigned char. Каждая машина имеет умолчание - каким должен быть char, таким, как unsigned char или таким, как signed char. В идеале, переносимая программа всегда должна использовать unsigned char или signed char, когда она зависит от знаковости объекта. Но многие программы были написаны с использованием простого char, в предположении, что он будет либо знаковым, либо беззнаковым, в зависимости от машины, для которой они были написаны. Эта опция и ей противоположная позволяют вам заставить такую программу работать при противоположном умолчании. Тип char всегда отличен и от signed char, и от unsigned char, хотя его поведение всегда такое же, как у одного из этих двух типов. -fsigned-char Тип char считается знаковым, как signed char. Заметим, что эта опция эквивалентна '-fno-unsigned-char', - 17 - которая является отрицательной формой '-funsigned-char'. Анологично, опция '-fno-signed-char' эквивалентна '-funsigned-char'. -fsigned-bitfields -funsigned-bitfields -fno-signed-bitfields -fno-unsigned-bitfields Эти опции управляют знаковостью поля битов, в том случае, если его объявление не использует signed или unsigned. По умолчанию такие поля считаются знаковыми, потому что это последовательно: базовые целые типы такие, как int являются знаковыми. Однако, если используется опция '-traditional', битовые поля все беззнаковые - не важно какие. -fwritable-strings Строковые константы сохраняются в сегменте, доступном для записи и не сливаются. Это нужно для совместимости со старыми программами, которые предполагают, что можно писать в строковую константу. Опция '-traditional' также имеет такой эффект. Запись в строковые константы это очень плохая идея - "константы" должны быть константами. -fallow-single-precision Числа с плавающей точкой одинарной точности не расширяются до двойной при математических операциях, даже при компиляции с опцией '-traditional'. Классический C Кернигана и Риттчи расширяет все операции с плавающей точкой до двойной точности, вне зависимости от размеров операндов. На архитектуре, для которой вы компилируете одинарная точность может оказаться быстрее двойной. Если вы должны использовать '-traditional', но хотите использовать операции одинарной точности, когда операнды одинарной точности, используйте эту опцию. Эта опция не имеет эффекта при компиляции с ANSI или GNU C соглашениями. 2.4. Опции для Включения или Подавления Предупреждений - 18 - Предупреждения - это диагностические сообщения, которые сообщают о конструкциях не являющихся заведомо ошибочными, но рискованных или, вероятно, содержащими ошибку. Вы можете включить выдачу многих специфических предупреждений с помощью опций, начинающихся с '-W', например, '-Wimplicit', чтобы включить предупреждения на неявных объявлениях. Каждая из таких опций имеет отрицательную форму, которая начинается с '-Wno-', и служит для подавления выдачи предупреждений; например, 'Wno-implicit'. В этом руководстве указана только одна форма каждой опции - та, которая не является умолчанием. Следующие опции управляют количеством и видом предупреждений, выдаваемых GNU CC. -fsyntax-only Проверяется код на наличие синтаксических ошибок, но после этого не делается ничего. -pedantic Выдаются все предупреждения, требуемые строгим ANSI стандартом C, отбрасываются все программы, которые используют запрещенные расширения. Правильные ANSI C программы корректно компилируются как с, так и без этой опции (хотя очень редкие и требуют опции '-ansi'). Однако, без этой опции поддерживаются также некоторые GNU расширения и свойства традиционного C. С этой опцией они отклоняются. '-pedantic' не вызывает предупреждения при использовании альтернативных ключевых слов, начинающихся и заканчивающихся с '__'. Предупреждения также не появляются после выражений, переж которыми стоит '__extension__'. Однако, этот исключительный способ следует использовать только в системных заголовочных файлах; следует избегать его в прикладных программах. См. раздел 6.34 [Альтернативные Ключевые Слова]. - 19 - Эта опция не претендует на полезность - она существует только чтобы удовлетворить педантов, которые в противном случае будут утверждать, что GNU CC не может поддерживать стандарт ANSI C. Некоторые пользователи пытаются использовать '-pedantic', чтобы проверить программу на соответствие со строгим стандартом ANSI C. Они часто обнаруживают, что она делает не ровно то, что они хотят: она выявляет некоторые не ANSI C конструкции, но не все, а только те, для которых ANSI C требует диагностику. Возможность выявить все несоответствия с ANSI C могла бы быть полезна во многих случаях, но потребовала бы гораздо больше дополнительной работы совершенно отличной от целей опции '-pedantic'. Мы скорее рекомендуем пользователю воспользоваться преимуществами расширений GNU C и игнорировать ограничения других компиляторов. Кроме некоторых суперкомпьютеров и крайне маленьких машин, есть весьма мало причин пользоваться каким либо другим компилятором C вместо раскручиваемого компилятора GNU CC. -pedantic-errors Подобна опции '-pedantic', с тем отличием, что вместо предупреждений порождаются ошибки. -w Отменяются все предупреждения. -Wno-import Отменяются предупреждения об использовании '#import'. -Wchar-subscripts Предупреждает, если индекс массива имеет тип char. Это частая причина ошибок, так как программисты часто забывают, что этот тип может быть знаковым на некоторых машинах. -Wcomment Предупреждает при каждом появлении начала комментария '/*' в комментарии. - 20 - -Wformat Проверяет вызовы к printf, scanf и т. д., чтобы проверить типы передаваемых параметров на соответствие со специфицированной строкой формата. -Wimplicit Предупреждает, когда функция или параметр объявляются неявно. -Wparentheses Предупреждает об отсутствии скобок в некоторых контекстах, таких как присваивание там, где нужно значение или вложенные операции, в приоритетах которых люди часто путаются. -Wreturn-type Предупреждает, если функция объявлена с типом возвращаемого значения по умолчанию - int. Также предупреждает об операторах возврата без возвращаемого значения в функциях, чей возвращаемый тип не есть void. -Wswitch Предупреждает, когда оператор switch имеет в качестве переключателя тип перечисления, и для одного или нескольких именованных кодов отсутствует case. (Присутствие метки default подавляет это предупреждение.) Метка case вне диапозона перечисления также вызывает прежупреждение при использовании этой опции. -Wtrigraphs Предупреждает, если встречается какая-либо трехсимвольная ANSI C последовательность (предполагается, что они включены). -Wunused Предупреждает, если переменная не используется вне ее описания, или если функция объявлена static, но нигде не определена, или если метка объявлена, но не используется, или если оператор вычисляет значение, которое точно нигде не используется. Чтобы подавить выдачу такого предупреждения на выражении просто поставьте перед ним приведение к void. Для не используемых - 21 - переменных и параметров используйте атрибут 'unused' (см. раздел 6.28 [Атрибуты Переменных]). -Wuninitialized Автоматическая переменная используется без инициализации. Это предупреждение возможно только в оптимизирующей компиляции, поскольку требуемый анализ потока данных выполняется только при оптимизации. Если вы не используете опцию '-O', вы просто не получите эти предупреждения. Эти предупреждения выдаются только для переменных, которые являются кандидатами на выделение регистров. Следовательно, они не выдаются для тех переменных, которые объявлены volatile, или тех, чей адрес берется, или чей размер отличен от 1, 2, 4 или 8 байт. Они, также, не выдаются для структур, объединений и массивов, даже если они размещаются на регистрах. Заметим, что может не быть предупреждения о переменной, которая используется только чтобы вычислить значение, которое более не используется, потому что это вычисление может быть удалено анализом потока данных раньше, чем печатаются предупреждения. Эти предупреждения сделаны необязательными, потому что GNU CC недостаточно умен, чтобы видеть все случаи, при котрых код может быть правильным, несмотри на кажущуюся ошибку. Ниже показан один пример, как такое может случиться: { int x; switch (y) { case 1: x = 1; break; - 22 - case 2: x = 4: break; case 3: x = 5; } foo (x); } Если значение y всегда есть 1, 2 или 3, тогда x всегда иниацализирована, но GNU CC не знает этого. Ниже показан другой общий случай: { int save_y; if (change_y) save_y = y, y = new_y; ... if (change_y) y = save_y; } Здесь нет ошибки, потому что save_y используется, только если она была установлена. Некоторые ложные предупреждения могут быть устранены, если вы объявите все используемые функции, которые никогда не возвращают управление, как noreturn. См. раздел 6.22 [Атрибуты Функций]. -Wall Все вышеперечисленные опции '-W' вместе взятые. - 23 - Остающиеся опции '-W...' не включаются в '-Wall', потому что они предупреждают о конструкциях, которые мы считаем разумным использовать при случае в хороших программах. -W Печатает дополнительные предупреждения при следующих случаях: * Не volatile автоматическая переменнная может измениться при longjump'е. Эта опция также возможна только при оптимизирующей компиляции. Компилятор смотрит только на вызовы setjump. Он не может знать, где будет вызвана longjump; в действительности, обработчик сигнала может вызвать ее в любом месте кода. В результате вы можете получить предупреждение, даже если на самом деле нет никаких проблем, потому что longjump вызывается в месте, которое не порождает проблемы. * Функция может как возвращать, так и не возвращать значение. (Попадание в конец тела функции рассматривается как возврат без значения.) К примеру, следующяя функция вызывает данное предупреждение: foo (a) { if (a > 0) return a; } * Выражение в левой части операции ',' не содержит побочных эффектов. Чтобы подавить выдачу этого предупреждения, приведите неиспользуемое выражение к void. Например, выражение x[i,j] вызовет данное предупреждение, а выражение x[(void)i,j] - нет. * Беззнаковое значение сравнивается с нулем с помощью '<' или - 24 - '<='. * Появляется выражение сравнения вида 'x<=y<=z'; оно эквивалентно '(x<=y ? 1 : 0) <= z', которое отлично от интерпретации обычной математической нотации. * Класс памяти вроде static не является первым словом в объявлении. В соответствии со стандартом C такое использование является устаревшим. * Если указана также опция '-Wall' или '-Wunused', предупреждает о не используемых аргументах. * Агрегат имеет инициализатор с частью скобок. Например, следующий код вызывает данное предупреждение из-за отсутствия скобок вокруг инициализатора x.h: struct s { int f, g; }; struct t { struct s h; int i; }; struct t x = { 1, 2, 3 }; -Wtraditional Предупреждает об определенных конструкциях, которые ведут себя неодинаково в классическом и ANSI C. * Макроаргумент в строковой константе, появившийся в теле макроса. Это вызывает подстановку аргумента в классическом C, но в ANSI C аргумент остается частью строки. * Функция объявлена внешней в блоке и использована после конца блока. * Оператор switch имеет операнд типа long. -Wshadow Предупреждает, когда одна локальная переменная затеняет другую локальную переменную. - 25 - -Wid-clash-длина Предупреждает, когда у двух различных идентификаторов совпадают первые символы в количестве, определяемым параметром "длина". Это может вам помочь подготовить программу к компиляции устаревшим, ненормальным компилятором. -Wlager-than-длина Предупреждает, когда объявляется объект размелом больше чем "длина" байт. -Wpointer-arith Предупреждает обо всем, что зависит от размеров типа функции и типа void. GNU C назначает этим типам размер 1 для удобства вычислений с указателями на void и на функции. -Wbad-function-cast Предупреждает, когда вызов функции приводится к несоответствующему типу. Например, вызов int malloc () приводится к указателю. -Wcast-qual Предупреждает, когда указатель приводится так, чтобы убрать квалификатор типа. Например, const char * приводится к обычному char *. -Wcast-align Предупреждает, если указатель приводится так, что возрастают требования на выравнивание. Например, если char * приводится к int * на машине, где int может размещаться только по двух- или четырехбайтовой границе. -Wwrite-strings Дает строковым константам тип const char[длина] так, что копирование адреса в не константный указатель на char вызовет предупреждение. Эти предупреждения помогут вам найти код времени компиляции, который может пытаться писать в строковую константу, но только в том случае, если вы будете внимательно относиться к использованию const в объявлениях и прототипах. В противном случае это - 26 - превратится только в неприятности; вот почему мы не заставляем '-Wall' вызывать эти предупреждения. -Wconversion Предупреждает, если прототип вызывает преобразование типа отличное от того, которое было бы с тем же аргументом при отсутствии прототипа. Это включает преобразования целочисленных типов в плавающие и т. п., и преобразования, меняющие размер или знаковость целочисленного аргумента, кроме тех, которые совпадают с целочисленным расширением. Также предупреждает, если отрицательная целая константа неявно приводится к беззнаковому типу. Например, предупреждает об присваивании x = -1, если x беззнаковое. Но не предупреждает об прямо указаном преобразовании типа (unsigned)-1. -Wagregate-return Предупреждает, если определяется или вызывается функция, которая возвращает структуру. (В языках, где можно возвращать массивы, это также вызывает предупреждение.) -Wstrict-prototypes Предупреждает, если функция объявлена или определена без спецификации типов аргументов. (Определение функции в старом стиле не вызывает предупреждения, если перед ним было объявление с указанными типами аргументов.) -Wmissing-prototypes Предупреждает, если глобальная функция определена без предварительного объявления прототипа. Это предупреждение появляется, даже если само определение дает прототип. Цель этого предупреждения в том, чтобы выявить глобальные функции, которые не объявлены в заголовочных файлах. -Wredundant-decls Предупреждает, если нечто объявлено более чем один раз в одной области действия даже в тех местах, где многократные объявления допустимы и ничего не меняют. - 27 - -Wnested-externs Предупреждает, если extern объявление встречается дважды внутри функции. -Winline Предупреждает, если функция не может быть сделана inline и при этом была объявлена inline, или же была дана опция '-finline-functions'. -Werror Превращает все предупреждения в ошибки. 2.5. Опции для Отладки Ваших Программ или GNU CC GNU CC имеет различные специальные опции, которые используются для отладки как вашей программы, так и GCC. -g Порождает отладочную информацию в родном формате операционной системы (stabs, COFF, XCOFF или DWARF). GDB может работать с этой отладочной информацией. В большинстве систем, которые используют формат stabs, '-g' включает использование дополнительной отладочной информации, которую может использовать только GDB; эта дополнительная отладочная информация делает работу отладки в GDB лучше, но может, вероятно испортить работу других отладчиков, или помешать им прочитать программу. Если вы хотите управлять тем, будет ли порождаться дополнительная информация, используйте '-gstabs+', '-gstabs', '-gxcoff+', '-gxcoff', '-gdwarf+', '-gdwarf' (см. ниже). В отличие от большинства других компиляторов C, GNU CC позволяет использовать опцию '-g' вместе с '-O'. Сокращения кода сделанные при оптимизации могут случайно привести к неожиданным результатам: некоторые переменные, которые вы объявили могут не существовать вовсе; поток управления может переместиться, когда вы этого не ожидали; некоторые операторы могут не выполняться, потому что они производят - 28 - постоянный результа, или же их результат уже известен; некоторые операторы могут выполняться в других местах, потому что их вынесли из циклов. Тем не менее, GNU CC дает возможность отлаживать оптимизированный результат. Это делает разумным использование оптимизатора для отладки программ, которые могут содержать ошибки. Следующие опции полезны, если GNU CC сгенерирован с возможностью использовать более чем один формат отладочной информации. -ggdb Порождает отладочную информацию в родном формате (если он поддерживается), включающую расширения GDB, если это вообще возможно. -gstabs Порождает отладочную информацию в формате stabs (если он поддерживается) без расширений GDB. Это формат, используемый DBX'ом в большинстве BSD систем. На MIPS, Alpha и в системе System V Release 4 эта опция порождает отладочную информацию в формате stabs, которую не понимает DBX или SDB. В системе System V Realease 4 эта опция требует ассемблера GNU. -gstabs+ Порождает отладочную информацию в формате stabs (если он поддерживается) с использованием расширений GNU, которые понимает только отладчик GNU (GDB). Использование этих расширений с другими отладчиками вероятно приведет к их краху или помешает прочитать программу. -gcoff Порождает отладочную информацию в формате COFF (если он поддерживается). Этот формат использовался SDB в системах System V до System V Release 4. -gxcoff Порождает отладочную информацию в формате XCOFF (если он поддерживается). Это формат, используемый отладчиком DBX в системах - 29 - IBM RS/6000. -gxcoff+ Порождает отладочную информацию в формате XCOFF (если он поддерживается) с использованием расширений GNU, которые понимает только отладчик GNU (GDB). Использование этих расширений с другими отладчиками вероятно приведет к их краху или помешает прочитать программу, а также может привести ассемблер, отличный от ассемблера GNU (GAS), к ошибке. -gdwarf Порождает отладочную информацию в формате DWARF (если он поддерживается). Этот формат используется SDB в большинстве систем System V Release 4. -gdwarf+ Порождает отладочную информацию в формате DWARF (если он поддерживается) с использованием расширений GNU, которые понимает только отладчик GNU (GDB). Использование этих расширений с другими отладчиками вероятно приведет к их краху или помешает прочитать программу. -gуровень -ggdbуровень -gstabsуровень -gcoffуровень -gxcoffуровень -gdwarfуровень Включает отладочную информацию, а также использует 'уровень', чтобы определить как много информации требуется. По умолчанию используется уровень 2. Уровень 1 выводит минимальную информацию, достаточную, чтобы отслеживать цепочку вызовов в частях программы, которые вы не собираетесь отлаживать. Она включает в себя описание функций и внешних переменных, но не содержит информации об локальных переменных и номерах строк. - 30 - Уровень 3 включает дополнительную информацию, такую как все макро определения встречающиеся в программе. Некоторые отладчики поддерживают макро расширения при использовании '-g3'. -p Порождает дополнительный код для записи профилирующей информации, подходящей для анализирующей программы prof. Вы должны использовать эту опцию при компиляции исходного файла, о котором вы хотите получить информацию, и вы также должны использовать ее при линковке. -pg Порождает дополнительный код для записи профилирующей информации, подходящей для анализирующей программы gprof. Вы должны использовать эту опцию при компиляции исходного файла, о котором вы хотите получить информацию, и вы также должны использовать ее при линковке. -a Порождает дополнительный код для записи профилирующей информации для базисных блоков, который будет записывать, сколько раз выполняется каждый базисный блок, его начальный адрес и имя функции, которая его содержит. Если используется опция '-g', будут также записываться номер строки и имя файла начала базисного блока. Если не изменено описанием архитектуры машины, то действием по умолчанию является добавление к текстовому файлу 'bb.out'. Эти данные могут быть проанализированы программой вроде tcov. Заметим, однако, что формат данных не таков, как ожидает tcov. GNU gprof со временем будет расширен для обработки этих данных. -dбуквы Делает отладочные дампы во время компиляции в моменты времени определяемые буквами. Они используются для отладки компилятора. Имена файлов для большинства дампов получаются из имени исходного файла добавлением слова (например, 'foo.c.rtl' или 'foo.c.jump'). Ниже указаны возможные буквы и их значения: 'M' Дамп всех макро определений в конце препроцессирования; не - 31 - порождать другого вывода. 'N' Дамп всех макро определений в конце препроцессирования. 'D' Дамп всех макро определений в конце препроцессирования, в добавление к нормалному выводу. 'y' Дамп отладочной информации во время лексического разбора в стандартный вывод ошибок. 'r' Дамп после генерации RTL в файл 'файл.rtl'. 'x' Только генерировать RTL для функции вместо ее компиляции. Обычно используется вместе с 'r'. 'j' Дамп после первой оптимизации переходов в файл 'файл.jump'. 's' Дамп после CSE (включая оптимизацию переходов, которая иногда следует за CSE) в файл 'файл.cse'. 'L' Дамп после оптимизации циклов в файл 'файл.loop'. 't' Дамп после второго прохода CSE (включая оптимизацию переходов, которая иногда следует за CSE) в файл 'файл.cse2'. 'f' Дамп после анализа потока в файл 'файл.flow'. 'c' - 32 - Дамп после комбинирования инструкций в файл 'файл.combine'. 'S' Дамп после первого прохода планировщика в файл 'файл.sched'. 'l' Дамп после локального распределения регистров в файл 'файл.lreg'. 'g' Дамп после глобального распределения регистров в файл 'файл.greg'. 'R' Дамп после второго прохода планировщика в файл 'файл.sched2'. 'J' Дамп после последней оптимизации переходов в файл 'файл.jump2'. 'd' Дамп после планировщика ветвей в файл 'файл.dbr'. 'k' Дамп после преобразования регистров в стек в файл 'файл.stack'. 'a' Порождает все дампы, перечисленные выше. 'm' Печатает статистику использования памяти в конце исполнения в стандартный вывод ошибок. 'p' Комментирует ассемблерный вывод комментариями, показывающими какой шаблон и какая альтернатива были использованы. -fpretend-float При запуске кросс-компилятора делает вид, что целевая машина использует тот же формат чисел с плавающей точкой, что и базовая машина. Это приводит к неверному выводу реыльных констант с плавающей - 33 - точкой, но реальная последовательность инструкций будет, вероятно, такой же как и последовательность, порождаемая GNU CC при запуске на целевой машине. -save-temps Постоянно сохраняет промежуточные "временные" файлы; они помещаются в текущий каталог, а их имена основываются на имени исходного файла. Так, компилируя 'foo.c' с опциями '-c -save-temps', будут порождены файлы 'foo.i', 'foo.s', также как и 'foo.o'. -print-file-name=библиотека Печатает полное абсолютное имя библиотечного файла 'библиотека', которое использовалось бы при линковке, и не делает больше ничего. С этой опцией GNU CC ничего не компилирует и не линкует - он только печатает имя файла. -print-prog-name=программа Подобна '-print-file-name', но ищет программу, такай как, например, 'cpp'. -print-libgcc-file-name Совпадает с '-print-file-name=libgcc.a'. Эта опция полезна, когда вы используете '-nostdlib' или '-nodefaultlibs', но вы хотите линковать с 'libgcc.a'. Вы можете написать gcc -nostdlib файлы... 'gcc -print-libgcc-file-name'. -print-search-dirs Печатает имя сконфигурированной инсталяционной директории, список программ и библиотечных директориев, которые будет просматривать gcc и не делает больше ничего. Эта опция полезна, когда gcc печатает сообщение об ошибке 'installation problems, cannot exec cpp: No such file or directory' ('проблемы инсталяции, невозможно выполнить cpp: Нет такого файла или директория'). Чтобы разрешить эту проблему, вы либо должны поместить 'cpp' и другие компоненты компилятора там, где gcc ожидает - 34 - их найти, либо вы можете установить переменную окружения GCC_EXEC_PREFIX равной дериктории, где вы их поместили. Не забывайте сопутствующие символы '/'. См. раздел [Переменные Окружения]. 2.6. Опции, которые Управляют Оптимизацией Эти опции управляют различными видами оптимизаций: -O -O1 Оптимизировать. Оптимизированная трансляции требует несколько больше времени и несколько больше памяти для больших функций. Без `-O' цель компилятора состoит в том, чтобы уменьшить стоимость трансляции и выдать ожидаемые результаты при отладке. Операторы независимы: если вы останавливаете программу на контрольной точке между операторами, вы можете назначить новое значение любой переменной или поставить счетчик команд на любой другой оператор в функции и получить точно такие результаты, которые вы ожидали из исходного текста. Без `-O' компилятор только распределяет переменные объявленные register в регистрах. Получающийся в результате откомпилированный код немного хуже чем произведенный PCC без `-O'. С `-O' компилятор пробует уменьшить размер кода и время исполнения. Когда вы задаете `-O', компилятор включает `-fthread-jumps' и `-fdefer-pop' на всех машинах. компилятор включает `-fdelayed-branch' на машинах, которые имеют щели задержки, и `-fomit-frame-pointer' на машинах, которые могут обеспечивать отладку даже без указателя фрейма. На некоторых машинах компилятор также включает другие флаги. -O2 Оптимизирует даже больше. GNU CC выполняет почти все поддерживаемые оптимизации, которые не включают уменьшение времени исполнения за счет увеличения длины кода. Компилятор не выполняет раскрутку циклов или подстановку функций, когда вы указываете `-O2'. - 35 - По сравнения с `-O' эта опция увеличивает как время компиляции, так и эффективность сгенерированного кода. `-O2' включает все необязательные оптимизации кроме раскрутки циклов и подстановки функций. Она также включает опцию `-fforce-mem' на всех машинах и исключение указателя фрейма на машинах, где это не помешает отладке. -O3 Оптимизирует еще больше. `-O3' включает все оптимизации, определяемые `-O2', а также включает опцию `inline-functions'. -O0 Не оптимизировать. Если вы используете многочисленные `-O' опции с номерами или без номеров уровня, действительной является последняя такая опция. Опции вида `-fфлаг' определяют машинно-независимые флаги. Большинство флагов имеют положительную и отрицательную формы; отрицательной формой `-ffoo' будет `-fno-foo'. В таблице, расположенной ниже, указана только одна из форм - та, которая не является умолчанием. Вы можете сконструировать другую форму удалением или добавлением '-no'. -ffloat-store Не позволяет сохранять переменные с плавающей точкой в регистрах и препятствует другим опциям, которые могут изменять, откуда берется значение с плавающей точкой - из регистра или из памяти. Эта опция предотвращает нежелательную излишнюю точность на машинах типа 68000, где регистры с плавающей точкой (из 68881) хранят большую точность, чем имеет double. Для большинства программ, излишняя точность делает только лучше, но некоторые программы полагаются на точное описание плавающей точки IEEE. Используйте `-ffloat-store' для таких программ. -fno-defer-pop - 36 - Всегда выталкивает параметры каждого вызова функции сразу после возврата из нее. Для машин, которые должны выталкивать параметры после обращения к функции, компилятор обычно позволяет параметрам накапливаться на стеке при нескольких вызовах функций, и выталкивает их все сразу. -fforce-mem Заставляет копировать операнды в памяти в регистры перед выполнением с ними арифметических операций. Это производит лучший код, поскольку все ссылки становятся потенциальными общими подвыражениями. Если они не являются общими подвыражениями, комбинацирование команд должно исключить отдельную загрузку регистров. Опция '-O2' включает эту опцию. -fforce-addr Заставляет копировать константные адреса памяти в регистры перед выполнением с ними арифметических операций. Это производит лучший код точно также как и '-fforce-mem'. -fomit-frame-pointer' Не хранит указатель фрейма для функций, которые не нуждаются в нем. Это избегает команд сохранения, установки и восстановления указателя фрейма; это также делает доступным дополнительный регистр во многих функциях. Это делает отладку невозможной на некоторых машинах. На некоторых машинах, таких как Vax, этот флажок не имеет эффекта, потому что стандартная вызывающая последовательность автоматически обрабатывает указатель фрейма, и ничего не экономится, если считается, что он не существует. Макрос `FRAME_POINTER_REQUIRED' в описании архитектуры управляет тем, обеспечивает ли целевая машина этот флаг или нет. См. раздел [Регистры]. -fno-inline Не обращать внимание на ключевое слово inline. Обычно эта опция используется, чтобы уберечь компилятор от подстановки любых функций. Обратите внимание что, если вы не оптимизируете, никакие функции не могут быть подставлены. - 37 - -finline-functions Интегрирует все простые функции в вызывающие функции. Компилятор эвристически решает, какие функции достаточно просты, чтобы их стоило интегрировать таким образом. Если все вызовы к данной функции интегрированы, а функция обьявлена static, тогда ассемблерный код функции обычно не выводится в своем настоящем виде. -fkeep-inline-functions Даже если все вызовы к данной функции интегрированы, и функция обьявлена static, выводит, однако, отдельную вызываемую во время выполнения версию функции. -fno-function-cse Не кладет адреса функций в регистры; каждая инструкция, которая вызывает постоянную функцию явно содержит адрес функции. Эта опция приводит к менее эффективному коду, но некоторые странные куски, которые изменяют ассемблерный код, могут быть спутаны оптимизацией, выполняемой, когда эта опция не используется. -ffast-math Эта опция позволяет GCC нарушать некоторые ANSI или IEEE правила и/или спецификации с целью оптимизации быстродействия кода. Наример, она позволяет компилятору считать, что параметры функции sqrt являются неотрицательными числами, и что никакие значения с плавающей точкой не являются NaNs. Эта опция не должна никогда включаться любой из `-O' опций, так как она может приводить к неправильному результату для программ, которые зависят от точного выполнения правил/спецификаций IEEE или ANSI для математических функций. Следующие опции управляют специфическими оптимизациями. Опция `-O2' включает все эти оптимизации за исключением `-funroll-loops' и - 38 - `-funroll-all-loops'. На большинстве машин опция '-O' включает опции `-fthread-jumps' и `-fdelayed-branch', но отдельные машины могут обрабатывать ее по-другому. Вы можете использовать следующие флаги в тех редких случаях, когда желательно выполнять "тонкую настройку" оптимизации. -fstrength-reduce Выполняет оптимизацию циклов: понижение силы операций и исключение итеративных переменных. -fthread-jumps Выполняет оптимизацию условного перехода, если выясняется, что он переходит к участку, где находится другое сравнение подчиненное первому. Если так, то первый переход переназначается на точку назначения второго перехода или точку сразу после него, в зависимости от того, должно ли быть второе условие истинным или ложным. -fcse-follow-jumps В исключении общих подвыражений проходит через команду перехода, если точка назначения перехода не достигается никаким другим маршрутом. Например, когда CSE сталкивается с оператором if с ветвью else, CSE будет следовать переходу к случаю, когда проверяемое условие ложно. -fcse-skip-blocks Подобна `-fcse-follow-jumps', но заставляет CSE следовать переходам, которые условно пропускают блоки. Когда CSE сталкивается с простым оператором if без ветви else, `-fcse-skip-blocks' заставляет CSE следовать переходу вокруг тела if. -frerun-cse-after-loop Повторно запускается общее удаление подвыражений после оптимизации циклов. -fexpensive-optimizations Выполняет ряд малых оптимизации, которые являются относительно дорогими. - 39 - -fdelayed-branch Если поддерживается для целевой машины, пытается переупорядочивать инструкции так, чтобы эксплуатировать щели задержки доступные после задержанной команды перехода. -fschedule-insns Если обеспечивается для целевой машины, пытается переупорядочить инструкции так, чтобы исключить простои исполнения из-за того, что отсутствуют требуемые данные. Это помогает машинам, которые имеют медленные инструкции с плавающей точкой или загрузки памяти, при разрешении другим командам выполняться, пока результат загрузки или инструкции с плавающей точкой не требуется. -fschedule-insns2 Подобна `-fschedule-insns', но запрашивает дополнительный проход планировщика после того, как было выполнено распределение регистров. Это особенно полезно на машинах с относительно маленьким числом регистров и там, где команды загрузки памяти требуют больше чем один цикл. -fcaller-saves Дает возможность распредять значения в регистрах, которые затираются вызовами функций, с порождением дополнительных команд сохранения и восстановления регистров вокруг таких вызовов. Такое распределение выполняется только тогда, когда кажется, что оно приводит к лучшему коду чем другое. Эта опция включается по умолчанию на некоторых машинах, обычно на тех, которые не имеют никаких сохраняемых при вызове регистров для использования взамен. -funroll-loops Выполняет раскрутку циклов. Она выполненяется только для циклов, число итераций которых можно определить в процессе компиляции или во время выполнения. `-funroll-loop' подразумевает и `-fstrength-reduce' и `-frerun-cse-after-loop'. - 40 - -funroll-all-loops Выполняет раскрутку циклов. Она выполняется для всех циклов и обычно делает программы более медленными. `-funroll-all-loops' подразумевает `-fstrength-reduce', также как и `-frerun-cse-after-loop'. -fno-peephole Отключает любые peephole-оптимизации специфические для машины. 2.7. Опции Управляющие Препроцессором Эти опции управляют препроцессором C, который обрабатывает каждый C исходный файл перед фактической компиляцией. Если вы используете опцию `-E', ничего кроме препроцессирования не делается. Некоторые из этих опций имеют смысл только вместе с `-E', потому что они делают вывод препроцессора неподходящим для фактической компиляции. -include файл Обрабатывает 'файл' как ввод перед обработкой обычного входного файла. Фактически, содержимое 'файла' компилируется сначала. Любая опция '-D' или '-U' из командной строки обрабатывается до '-include файл', вне зависимости от порядка, в котором они записаны. Все опции '-include' и '-imacros' обрабатываются в том порядке, в котором они записаны. -imacros файл Обрабатывает 'файл' как ввод, отбрасывая возникающий в результате вывод до обработки обычного входного файла. Так как вывод сгенерированный из 'файла' отбрасывается, единственый эффект `-imacros файл' состоит в том, что макрокоманды определенные в 'файле' становятся доступны для применения в главном вводе. Любая опция '-D' или '-U' из командной строки обрабатывается до '-imacros файл', вне зависимости от порядка, в котором они записаны. Все опции '-include' и '-imacros' обрабатываются в том порядке, в котором они записаны. - 41 - -idirafter директорий Добавляют каталог 'директорий' ко второму маршруту включения. В каталогах второго маршрута включения ищут, когда заголовочный файл не обнаружен ни в одном из каталогов в главном маршруте включения (маршрут, к которому добавляет опция `-I'). -iprefix префикс Определяет 'префикс' как префикс для нижеследующей опции `-iwithprefix'. -iwithprefix директорий Добавляет каталог ко второму маршруту включения. Имя каталога получается объединением 'префикса' и 'директория', где 'префикс' определялся предварительно опцией `-iprefix'. Если вы еще не определили префикс, по умолчанию используется каталог, содержащий инсталированные проходы компилятора. -iwithprefixbefore директорий Добавляет каталог к главному маршруту включения. Имя каталога получается объединением 'префикса' и 'директория', как в случае `-iwithprefix'. -isystem директорий Добавляет каталог 'директорий' к началу второго маршрута включения, помечая его как системный каталог, так что он имеет ту же самую специальную обработку, что и стандартные системные каталоги. -nostdinc Не ищет в стандартных системных каталогах для заголовочных файлов. Только каталоги, которые вы определили опциями `-I' (и текущий каталог, если подходит) используется для поиска. См. раздел [Опции Ди- ректорий] для информации об '-I'. Используя `-nostdinc' и `-I-', вы можете ограничить маршрут поиска файлов включения только теми каталогами, которые вы задали явно. - 42 - -undef Не предопределяет любые нестандартные макросы. (Включая флаги архитектуры). -E Запускает только препроцессор C. Препроцессирует все указанные C исходные файлы и выводит результаты в стандартный вывод или в указанный выходной файл. -C Говорит препроцессору не отбрасывать комментарии. Используется с опцией `-E'. -P Говорит препроцессору не генерировать директивы `#line'. Используется с опцией '-E'. -M Говорит препроцессору выводить правила для make, описывающие зависимости каждого объектного файла. Для каждого исходного файла, препроцессор выводит одно make-правило, чья цель - имя объектного файла, для которого исходный файл и чьими зависимостями являются все #include заголовочные файлы, которые он использует. Это правило может быть одиночной строкой или может быть продолжено с помощью `\'-новая строка, если оно длинное. Список правил печатается в стандартный вывод вместо препроцессированной C программы. `-M' подразумевает `-E'. Другой способ указать вывод make-правил - с помощью установки переменной окружения DEPENDENCIES_OUTPUT (См. раздел [Переменные Окружения]). -MM Подобна `-M', но вывод упоминает только заголовочные файлы пользователя, включенные с помощью `#include "файл"'. Системные заголовочные файлы, включенные с помощью `#include <файл>' опускаются. - 43 - -MD Подобна `-M', но информация о зависимостях записывается в файл получающемуся при замене ".c" на ".d" на концах имен входных файлов. Это делается в добавление к указанной компиляции файла - `-MD' не запрещает обычную компиляцию, как это делает '-M'. В Mach вы можете использовать утилиту `md', чтобы обьединить многочисленные файлы зависимостей в один файл, подходящий для использования с командой make. -MMD Подобна `-MD', за исключением упоминания только пользовательских заголовочных файлов, но не системных заголовочных файлов. -MG Обрабатывать отсутствующие заголовочные файлы как генерируемые файлы и считать, что они находятся в том же самом каталоге, что и исходный файл. Если вы указываете `-MG', вы также должны указывать или `-M', или `-MM'. `-MG' не поддерживается с `-MD' или `-MMD'. -H Печатает имя каждого используемого заголовочного файла в добавление к нормальным действиям. -Aвопрос(ответ) Утвердить ответ 'ответ' для 'вопроса', в случае, если он проверяется условным выражением препроцессора типа `#if #вопрос(ответ)'. `-A-' отключает стандартные утверждения, которые обычно описывают целевую машину. -Dмакрос Определяет макрос 'макрос' со строкой `1' в качестве его определения. -Dмакрос=определение' Определяет макрос 'макрос' как 'определение'. Все экземпляры `-D' в командной строке обрабатываются до любых опций `-U'. - 44 - -Uмакрос Отменяют определение макроса 'макрос'. Опции `-U' обрабатываются после всех опций `-D', но перед любыми `-include' и `-imacros' опциями. -dM Говорит препроцессору вывести только список макроопределений, которые имеют действие в конце препроцессирования. Используется с опцией `-E'. -dD Говорит препроцессору передать все макроопределения в вывод в их последовательности в другом выводе. -dN Подобна `-dD', за исключением того, что макроаргументы и содержание опускаются. В вывод включается только `#define имя'. -trigraphs Поддерживает трехзнаковые последовательности ANSI C. Опция `-ansi' также имеет этот эффект. -Wp,опция Передает 'опцию' в качестве опции препроцессору. Если 'опция' содержит запятые, она расщепляется запятыми на многочисленные опции. 2.8. Передача Опций Ассемблеру Вы можете передать опции ассемблеру. -Wa,опция Передает 'опцию' в качестве опции ассемблеру. Если 'опция' содержит запятые, она расщепляется запятыми на многочисленные опции. 2.9. Опции Линковки Эти опции вступяют в игру тогда, когда компилятор линкует объектные файлы в выполнимый выходной файл. Они бессмысленны, если - 45 - компилятор не выполняет шаг линковки. имя-объектного-файла Имя файла, которое не заканчивается специальным распознаным суффиксом, считается именем объектного файла или библиотеки. (Объектные файлы отличаются линкером от библиотек по содержимому файла.) Если выполняется линковка, эти объектные файлы используются в качестве ввода для линкера. -c -S -E Если используется любая из этих опций, то линкер не запускается, и имена объектных файлов не должны использоваться в качестве параметров. См. раздел [Общие Опции]. -lбиблиотека Ищет при линковке библиотеку с именем 'библиотека'. Есть различие в том, где в комадной строке вы записываете эту опцию; линкер ищет обрабатываемые библиотеки и объектные файлы в порядке, в котором они указаны. Таким образом, `foo.o -lz bar.o' ищет библиотеку `z' после файла `foo.o', но перед `bar.o'. Если `bar.o' ссылается на функции в `z', эти функции не могут быть загружены. Линкер просматривает стандартный список каталогов в поиске библиотеки, который, фактически, является файлом с именем `libбиблиотека.a'. Затем линкер использует этот файл так, как будто бы он был точно специфицирован по имени. Директории, в которых ищет линкер, включают несколько стандартных системных каталогов, плюс любые каталоги, которые вы определяете с помощью `-L'. Обычно файлы, обнаруженные этим способом являются библиотечными файлами - архивными файлами, чьи элементы - объектные файлы. Линкер обрабатывает архивный файл, просматривая его в поиске элементов, которые определяют символы, на которые были ссылки, но которые до сих - 46 - пор не определялись. Но, если оказывается, что обнаруженный файл - обычный объектный файл, то он линкуется в обычном порядке. Единственное различие между использованием опции `-l' и указанием имени файла в том, что `-l' добавляет к 'библиотеке' `lib' и `.a' и ищет в нескольких директориях. -lobjc Вам нужен этот специальный случай опции `-l' для линковки Objective C программ. -nostartfiles Не использует стандартные системные файлы начального запуска при линковке. Стандартные системные библиотеки используются как обычно, если не указано `-nostdlib' или `-nodefaultlibs'. -nodefaultlibs Не использует стандартные системные библиотеки при линковке. Линкеру передаются только те библиотеки, которые вы указываете. Стандартные файлы начального запуска используются как обычно, если не указано `-nostartfiles'. -nostdlib Не использует стандартные системные файлы начального запуска и библиотеки при линковке. Никакие файлы запуска и только те библиотеки, которые вы указываете будут, переданы линкеру. Одной из стандартных библиотек, обходимых `-nostdlib' и `-nodefaultlibs' является `libgcc.a' - библиотека внутренних подпрограмм, которые использует GNU CC, чтобы преодолевать изъяны конкретных машин, или для специальных потребностей некоторых языков. (См. Главу 13 [Интерфейс к выводу GNU CC] для подробного рассмотрения `libgcc.a'.) В большинстве случаев, вы нуждаетесь в `libgcc.a', даже когда вы хотите избегать других стандартных библиотек. Другими словами, когда вы указываете `-nostdlib' или `-nodefaultlibs', вы должны обычно также указывать `-lgcc'. Это гарантирует, что нет никаких нарушеных ссылок к внутренним библиотечным подпрограммам GNU CC. ( Например, `__main' , используемая, чтобы гарантировать вызов конструкторов C++.) - 47 - -s Удаляет все символьные таблицы и информацию о перемещениях из исполняемого файла. -static В системах, которые поддерживают динамическую линковку, предотвращает линковку с разделяемыми библиотеками. В других системах, эта опция не имеет никакого эффекта. -shared Производит разделяемый объект, который может затем быть слинкован с другими объектами, чтобы сформироваться исполнимый файл. Только некоторые системы поддерживают эту опцию. -symbolic Связывает ссылки к глобальным символам при формировании разделяемого объекта. Предупреждает о любых неразрашенных ссылок (если не отменено опцией редактора связей `-Xlinker -z -Xlinker определения' ). Только некоторые системы поддерживают эту опцию. -Xlinker опция Передает 'опцию' в качестве опции линкеру. Вы можете использовать ее, чтобы поддерживать специфические для системы опции линкера, о которых GNU CC не знает как распознавать. Если вы хотите передать опцию, которая имеет параметр, вы должны использовать `-Xlinker' дважды: один раз для опции и один раз для параметра. Например, чтобы передать `-assert описания', вы должны написать `-Xlinker -assert -Xlinker описания'. Не срабатывает, если написать `-Xlinker " -assert описания " ', потому что это передает всю cтроку как единый параметр, который не ожидается линкером. -Wl,опция Передает 'опцию' в качестве опции линкеру. Если 'опция' содержит запятые, она расщепляется запятыми на многочисленные опции. -uсимвол - 48 - Делает вид, что символ 'символ' неопределен, вынуждая линкуемые библиотечные модули определять его. Вы можете использовать `-u' много разы с различными символами, чтобы вызвать загрузку дополнительных библиотечных модулей. 2.10. Опции для Поиска в Директориях Эти опции определяют каталоги для поиска заголовочных файлов, библиотек и частей компилятора: -Iдиректория Добавляет каталог 'директория' в начало списка каталогов, используемых для поиска заголовочных файлов. Ее можно использовать для подмены системных заголовочных файлов, подставляя ваши собственные версии, поскольку эти директории просматриваются до директорий системных заголовочных файлов. Если вы используете более чем одну опцию '-I', директории просматриваются в порядке слева направо; стандартные системные директории идут после. -I- Любые каталоги, которые вы указываете с опциями `-I' до опции `-I-' просматриваются только в случае `#include "файл"'; они не просматриваются для `#include <файл>'. Если дополнительные каталоги указаны с опциями `-I' после опции `-I-', эти каталоги просматриваются для всех директив `#include'. (Обычно все `-I' директории используются таким способом.) Кроме того, опция `-I-' запрещает использование текущего каталога (откуда пришел текущий входной файл) в качестве первого каталога для поиска `#include "файл"'. Нет никакого способа отменить этот эффект опции `-I-'. С помощью `-I.' вы можете указать поиск в каталоге, который был текущим, когда вызывался компилятор. Это не точно то же самое, что делает препроцессор по умолчанию, но часто подходит. '-I-' не запрещает использование стандартных системных директориев для заголовочных файлов. Таким образом, '-I-' и '-nostdinc' являются независимыми. - 49 - -Bпрефикс Эта опция указывает где искать выполнимые, библиотечные и заголовочные файлы, а также файлы данных для самого компилятора. Управляющая программа компилятора запускает одну или больше программ 'cpp', 'cc1', 'as' и 'ld'. Он пытается использовать 'префикс' в качестве префикса для каждой программы, которую он запускает, как с так и без 'машина/версия/' (см. раздел [Целевые Опции]). Для каждой запускаемой программы компилятор сначала пытается использовать префикс '-B', если указан. Если такое имя не найдено, или опция '-B' не указана, компилятор пытается использовать два стандартных префикса: '/usr/lib/gcc/' и '/usr/local/lib/gcc-lib/'. Если ни один из этих префиксов не дает результата, не модифицированное имя программы ищется с использованием директорий указанных в вашей переменной окружения 'PATH'. Префикс '-B', который эффективно указывает имена директорий, также применяется к библиотекам в линкере, поскольку компилятор преобразует эти опции в опции '-L' для линкера. Они, также, применяются к заголовочным файлам в препроцессоре, поскольку компилятор преобразует эти опции в опции '-isystem' для препроцессора. В этом случае компилятор добавляет 'include' к префиксу. Файл поддержки времени выполнения 'libgcc.a' может также искаться, если нужно, с использованием префикса '-B'. Если он не найден там, используются два стандартных префикса, указанных выше, и это все. Файл пропадает из линковки, если он не найден таким способом. Другой способ указать префикс очень сходный с префиксом '-B' - использовать переменную окружения GCC_EXEC_PREFIX. См. раздел [Переменные Окружения]. 2.11. Указание Целевой Машины и Версии Компилятора По умолчанию, GNU CC компилирует код для той же самой машины, которую вы используете. Однако, он также может быть инсталлирован как - 50 - кросс-компилятор, чтобы компилировать для какого-нибудь другого типа машин. В действительности, несколько различных конфигураций GNU CC, для различных целевых машин, могут быть установлены бок о бок. Затем, вы указываете, какую конфигурацию использовать с помощью опции '-b'. Кроме того, более старые и более новые версии компилятора могут быть установлены бок о бок. Одина из них (вероятно, самая новая) будет являться умолчанием, но вы можете захотеть иногда использовать другую. -b машина Аргумент 'машина' указывает целевую машину для компиляции. Она полезна, когда вы установили GNU CC в качестве кросс-компилятора. Значение, используемое в качастве аргумента 'машина' - то же, что было указано при конфигурировании GNU CC в качестве кросс-компилятора. Например, если кросс-компилятор конфигурировался с 'configure i386v', означающем компиляцию для 80386 с System V, тогда вы должны указать '-b i386v', чтобы запустить этот кросс-компилятор. Когда вы не указываете '-b', это обычно означает компиляцию для того же самого типа машины, который вы используете. -V версия Аргумент 'версия' указывает, какую версию компилятора запускать. Она полезна, когда установлены многочисленные версии. Например, 'версия' может быть '2.0', означая запуск GNU CC версии 2.0. Версией по умолчанию, когда вы не указываете '-V', является последняя версия GNU CC, которую вы установили. Опции '-b' и '-V' в действительности работают с помощью управления частью имен файлов, используемых для выполнимых файлов и библиотек, используемых при компиляции. Данная версия компилятор для данной целевой машины обычно храниться в директории '/usr/local/lib/gcc-lib/машина/версия'. Таким образом, можно установить действие опций '-b' и '-V', либо изменив имена этих директорий, либо добавив альтернативные - 51 - имена (или символические ссылки). Если в деректории '/usr/local/lib/gcc-lib/' файл '80386' является символической ссылкой на файл 'i386v', тогда '-b 80386' становиться синонимом '-b i386v'. С одной стороны, опции '-b' и '-V' не совсем переключают на другой компилятор: управляющая программа верхнего уровня gcc, которую вы первоначально вызвали, продолжает выполняться и вызывать другие исполнимые файлы (препроцессор, сам компилятор, ассемблер и линкер), которые и выполняют реальную работу. Однако, поскольку никакой реальной работы не выполняется в управляющей программе, обычно не важно, что используемая управляющая программа не является программой для указанной машины и версии. Единственное место, в котором управляющая программа зависит от целевой машины, в разборе и обработке специальных машинозависимых опций. Однако, это контролируется файлом, который находится, вместе с другими выполнимыми файлами, в директории для указанной версии и целевой машины. В результате, одна установленная управляющая программа адаптируется к любым указанным целевой машине и версии компилятора. Выполнимый файл управляющей программы, однако, управляет одной важной вещью: версией компилятора и целевой машиной по умолчанию. Следовательно, вы можете установить различные экземпляры управляющей программы, скомпилированные для разных целевых машин и версий, под различными именами. Например, если управляющая программа для версии 2.0 установлена как ogcc, а для версии 2.1 - как gcc, тогда команда gcc будет, по умолчанию, использовать версию 2.1, а ogcc - версию 2.0. Однако, вы можете выбрать любую версию с любой командой с помощью опции '-V'. 2.12. Модели и Конфигурации Машин Ранее мы обсуждали стандартную опцию '-b', которая выбирает среди различных инсталлированных компиляторов для совершенно различных типов целевых машин, таких как Vax, 68000, 80386. В добавление, каждый из этих типов машин может иметь свои - 52 - собственные специальные опции, начинающиеся с '-m', для выбора между различными моделями и конфигурациями машин. Например, 68010 или 68020, плавающий сопроцессор или нет. Одна установленная версия компилятора может компилировать для любой модели и конфигурации, в соответствии с указанными опциями. Некоторые конфигурации компилятора также поддерживают дополнительные специальные опции, обычно для совместимости с другими компиляторами для той же платформы. Эти опции определяются с помощью макроса TARGET_SWITCHES в описании архитектуры. Умолчания для опций также определяются этим макросом, который позволяет вам менять умолчания. 2.12.1. Опции Intel 386 Эти '-m' опции определены для семейства i386 компьютеров: -m486 -m386 Управляет тем, оптимизируется ли код для 486 вместо 386 или нет. Код сгенерированный для 486 будет работать на 386 и наоборот. -mieee-fp -mno-ieee-fp Управляет тем, использует или нет компилятор IEEE сравнения с плавающей точкой. Они корректно обрабатывают случай, когда результат сравнения неопределен. -msoft-float Порождает выход, содержащий библиотечные вызовы для плавающей точки. Предупреждение: необходимая библиотека не является частью GNU CC. Обычно используются способности обыкновенных компиляторов C, но это не может быть сделано прямо в кросс-компиляции. Вы должны провести свое собственное мероприятие, чтобы обеспечить подходящюю библиотеку функций для кросс-компиляции. На машинах, где функции, возвращающие значение с плавающей - 53 - точкой, выдают результат на регистровом стеке 80387, некоторые инструкции с плавающей точкой могут порождаться даже при использовании '-msoft-float'. -mno-fp-ret-in-387 Не использует регистру плавающего сопроцессора для возврата значений из функций. Обычная конвенция вызовов возвращает значения типов float и double на регистрах плавающего сопроцессора, даже, если плавающего сопроцессора нет. Идея состоит в том, что операционная система должна эмулировать плавающий сопроцессор. Опция '-mno-fp-ret-in-387' заставляет вместо этого возвращать такие значения на обычных регистрах центрального процессора. -mno-fancy-math-387 Некоторые эмуляторы 387 не поддерживают инструкции sin, cos и sqrt для 387. Указывая эту опцию, вы избегаете генерации этих инструкций. Эта опция включается по умолчанию на FreeBSD. В качестве исправления 2.6.1, эти инструкции не генерируются, если вы не используете, также, опцию '-ffast-math'. -malign-double -mno-align-double Управляет тем, выравнивает ли GNU CC double, long double и long long переменные на границу двух слов или на границу одного слова. Выравнивание double переменных на границу двух слов порождает код, который выполняется немного быстрее на 'Pentium' при затрате большего количества памяти. Предупреждение: Если вы используете опцию '-malign-double', структуры, содержащие выше перечисленные типы будут выравниваться не так, как в опубликованных спецификациях прикладного двоичного интерфейса для 386. -msvr3-shlib -mno-svr3-shlib - 54 - Управляет тем, куда GNU CC кладет неинициализированные локальные переменные - в bss или data. 'msvr3-shlib' кладет эти переменные в bss. Эти опции имеют смысл только в System V Release 3. -mno-wide-multiply -mwide-multiply Управляет тем, использует ли GNU CC mul и imul, которые выдают 64-битный результат в eax:edx из 32-битных операндов для выполнения long long умножения и 32-битного деления на константы. -mrtd Использует другую конвенцию вызова функций, в которой функция, которая имеет фиксированное число аргументов возвращается с помощью инструкции 'ret число', которая выталкивает аргументы при возврате. Это сохраняет одну инструкцию в месте вызова, поскольку не надо выталкивать аргументы там. Вы можете указать, что отдельная функция вызывается с этой конвенцией вызова с помощью атрибута функции 'stdcall'. Вы можете, также, отменить опцию '-mrtd', используя атрибут функции 'cdecl'. См. раздел 6.22 [Атрибуты Функций]. Предупреждение: эта конвенция вызова несовместима с обычно используемой в Unix, так что вы не можете использовать ее, если вам нужно вызывать библиотеки скомпилированные компилятором Unix. Вы, также, должны обеспечить прототипы для всех функций, которые имеют переменное число аргументов (включая printf); иначе для вызова этих функций будет генерироваться неверный код. Кроме того, очень некорректный код будет сгенерирован, если вы вызовите функцию со слишком большим числом аргументов. (Обычно, лишние аргументы без последствий игнорируются.) -mreg-alloc=регистры Управляет порядком распределения по умолчанию целых регистров. Строка 'регистры' представляет собой ряд букв определяющих регистры. Поддерживаемыми буквами являются: a выделяет EAX, b выделяет EBX, - 55 - c выделяет ECX, d выделяет EDX, S выделяет ESI, D выделяет EDI, B выделяет EBP. -mreg-parm=число Управляет тем, сколько регистров используется для передачи целых аргументов. По умолчанию, регистры для передачи параметров не используются, и максимум 3 регистра могут быть использованы. Вы можете управлять этим для конкретных функций используя атрибут функции 'regparm'. См. раздел 6.22 [Атрибуты Функций]. Предупреждение: если вы используете эту опцию, и 'число' не равно нулю, вы должны строить все модули с одним и тем же значением, включая все библиотеки. Это включает системные библиотеки и модули начальной загрузки. -malign-loops=число Выравнивает циклы на границу степень двух с показателем 'число' байт. Если '-malign-loops' не указана, умолчанием является 2. -malign-jumps=число Выравнивает инструкции, на которые только переходят на границу степень двух с показателем 'число' байт. Если '-malign-jumps' не указана, умолчанием является 2 при оптимизации для 386, и 4 - для 486. -malign-functions=число Выравнивает начала функций на границу степень двух с показателем 'число' байт. Если '-malign-functions' не указана, умолчанием является 2 при оптимизации для 386, и 4 - для 486. 2.13. Опции Соглашений о Генерации Кода Эти машинонезависимые опции управляют соглашениями об интерфейсе, используемыми при генерации кода. Большинство из них имеет как положительную, так и отрицательную формы; отрицательной формой '-ffoo' будет '-fno-foo'. В таблице ниже, указывается только одна из этих форм - та, которая не является умолчанием. Вы можете получить другую форму, либо удалив 'no-', либо - 56 - добавив его. -fpcc-struct-return Возвращает "короткие" структуры и объединения в памяти, также как и длинные, а не на регистрах. Эта конвенция менее эффективна, но она имеет преимущество в совместимости вызовов между файлами, скомпилированными GNU CC, и файлами, скомпилированными другими компиляторами. Точная конвенция возврата структур в памяти зависит от макросов описания архитектуры. Короткими структурами и объединениями являются те, чей размер и выравнивание соответствует одному из целых типов. -freg-struct-return Использует соглашение, что структуры и объединения возвращаются, когда возможно. на регистрах. Это более эффективно для малых структур, чем '-fpcc-struct-return'. Если вы не указываете ни '-fpcc-struct-return', ни ее противоположность - '-freg-struct-return', GNU CC использует стандартную для данной архитектуры конвенцию. Если стандартной конвенции не существует, GNU CC использует '-fpcc-struct-return', кроме архитектур, где GNU CC является основным компилятором. В этом случае мы можем выбрать стандарт, и мы выбираем более эффективный вариант регистрового возврата. -fshort-enums Выделяет для типа перечисления только такое количество байтов, которое нужно для объявленного диапазона возможных значений. А именно, тип перечисления будет эквивалентен наименьшему целому типу, который имеет достаточно места. -fshort-double Использует тот же размер для double, что и для float. -fshared-data - 57 - Требует, чтобы данные и неконстантные переменные этой единицы компиляции были разделяемыми данными, а не личными. Это различие имеет смысл только для некоторых операционных систем, где разделяемые данные разделяются между процессами, выполняющимися в одной программе, в то время как личные данные существуют в одной копии на процесс. -fno-common Размешает даже неинициализированные глобальные переменные в секции bss объектного файла, вместо генерации их в качестве общих блоков.Это имеет эффект в том, что если одна и та же переменныя объявлена (без extern) в двух различных единицах компиляции, вы получети ошибку при их линковке. Единственной причиной, по которой это может быть полезно, является проверка того, что программа будет работать на других системах, которые всегда работают таким образом. -fno-ident Игнорирует директиву '#ident'. -finhibit-size-directive Не выводит ассемблерную директиву '.size', или что-нибудь еще, что может вызвать проблемы, если функция расщепляется посередине, и две половинки размещаются в областях, далеко отстоящих друг от друга в памяти. Эта опция используется при компиляции 'crtstuff.c'; вам не должно понадобиться использовать ее для чего-нибудь еще. -fverobose-asm Помещает дополнительную комментирующую информацию в ассемблерный код, чтобы сделать его более читаемым. Эта опция обычно нужна только тем, кому действительно нужно читать генерируемый ассемблерный код (может быть при отлаживании самого компилятора). -fvolatile Считает все ссылки в память через указатели volatile. -fvolatile-global Считает все ссылки в память на внешние и глобальные элементы данных volatile. - 58 - -fpic Порождает позиционно-независимый код (position-independent code - PIC), подходящий для использования в разделяемой библиотеке, если поддерживается для целевой машины. Такой код берет все константные адреса из глобальной таблицы смещений (global offset table - GOT). Если размер GOT для линкуемого выполняемого файла превышает специфичный для машины максимальный размер, вы получаете сообщение об ошибке от линкера, показывающее, что '-fpic' не работает; в этом случае перекомпилируйте с заменой на '-fPIC'. (Эти максимумы составляют: 16К на m88k, 8К на Sparc и 32К на m68k и RS/6000. 386 не имеет такого лимита.) Позиционно-независимый код требует специальной поддержки, и, следовательно, работает толька на некоторых машинах. Для 386, GNU CC поддерживает PIC для System V, но не для Sun 386i. Код генерируемый для IBM RS/6000 всегда позиционно-независимый. Ассемблер GNU не полностью поддерживает PIC. На данный момент, вы должны использовать какой-нибудь другой ассемблер, чтобы PIC работал. Мы бы приветствовали добровольцев, которые улучшили бы GAS, чтобы обрабатывать PIC; первой частью работы является изложить, что ассемблер должен делать иначе. -fPIC Если поддерживается для целевой машины, порождает позиционно независимый код, подходящий для динамической линковки и не имеющий никаких ограничений на размер глобальной таблицы смещений. Эта опция дает отличие на m68k, m88k и Sparc. Позиционно-независимый код требует специальной поддержки, и, следовательно, работает толька на некоторых машинах. -ffixed-регистр Обращается с регистром с именем 'регистр' как с фиксированным регистром; порождаемый код никогда не должен ссылаться на него (кроме, может быть, в качестве указателя стека, указателя фрейма или в какой-нибудь другой фиксированной роли). - 59 - 'регистр' должно быть именем регистра. Допускаемые имена регистров являются машинозависимыми и определяются макросом REGISTER_NAMES в заголовочном файле описания архитектуры. Эта опция не имеет отрицательной формы, потому что она определяет выбор из трех альтернатив. -fcall-used-reg Обращается с регистром с именем 'регистр' как с регистром, подходящим для распределения, который затирается вызовами функций. Он может выделяться для временных переменных или для переменных, которые не переживают вызов функции. Функции, откомпилированные с этой опцией, не будут сохранять и восстанавливать регистр 'регистр'. 'регистр' должно быть именем регистра. Допускаемые имена регистров являются машинозависимыми и определяются макросом REGISTER_NAMES в заголовочном файле описания архитектуры. Эта опция не имеет отрицательной формы, потому что она определяет выбор из трех альтернатив. -fcall-saved-регистр Обращается с регистром с именем 'регистр' как с регистром, подходящим для распределения, который сохраняется функциями. Он может выделяться даже для временных переменных или обычных переменных, которые переживают вызов функции. Функции, откомпилированные с этой опцией, будут сохранять и восстанавливать регистр 'регистр', если они его используют. Использование этого флага для регистра, который имеет фиксированную роль в машинной модели исполнения такую, как указатель стека или указатель фрейма, порождает разрушительный результат. Различные бедствия могут произойти от использования этого флага для регистров, в которых могут возвращаться значения функций. Эта опция не имеет отрицательной формы, потому что она определяет выбор из трех альтернатив. - 60 - -fpack-struct Упаковывает все члены структур рядом без зазоров. Обычно, не хотелось бы использовать эту опцию, поскольку она делает код не оптимальным, а смещения членов структур несоответствующим системным библиотекам. 2.14. Переменные Окружения, Затрагивающие GNU CC Этот раздел описывает несколько переменных окружения, которые затрагивают то как работает GNU CC. Они работают указывая директории и префиксы для использования при поиске различных типов файлов. Заметим, что вы также можете указать места для поиска, используя опции, такие как '-B', '-I' и '-L' (см. раздел [Опции Директорий]). Они имеют приоритет перед местами, указанными с помощью переменных ок- ружения, которые, в свою очередь, имеют приоритет перед местами, ука- занными конфигурацией GNU CC. См. раздел 17.1 [Управляющая Программа]. TMPDIR Если TMPDIR установлена, она указывает директорию, используемую для временных файлов. GNU CC использует временные файлы чтобы держать выход одной стадии компиляции, которая должна использоваться в качестве входа для следующей стадии: например, выход препроцессора, который является входом для собственно компилятора. GCC_EXEC_PREFIX Если GCC_EXEC_PREFIX установлена, она указывает префикс, используемый в именах программ, выполняемых компилятором. Косая черта не добавляется, когда этот префикс объединяется с именем программы, но вы можете указать префикс, который оканчивается косой чертой. Если GNU CC не может найти программу, используя указанный префикс, он пытается смотреть в обычные места для программ. Значением по умолчанию для GCC_EXEC_PREFIX является 'префикс/lib/gcc-lib/', где 'префикс' - значение prefix, когда вы - 61 - запускали 'configure'. Другие префиксы, указанные с помощью '-B', имеют приоритет перед этим префиксом. Этот префикс также используется для нахождения файлов, таких как 'crt0.o', который используется для линковки. Кроме того этот префикс используется необычным способом для нахождения директорий для поиска заголовочных файлов. Для каждой из стандартных директорий, чье имя обычно начинается с '/usr/local/lib/gcc-lib' (более точно, с значения GCC_INCLUDE_DIR), GNU CC пытается заменить это начало на указанный префикс, чтобы породить имя альтернативной директории. Таким образом, с '-Bfoo/' GNU CC будет искать 'foo/bar', если обычно он искал бы 'usr/local/lib/bar'. Эта альтернативная директория просматривается первой, затем идут стандартные директории. COMPILER_PATH Значением COMPILER_PATH является список директорий, разделенных двоеточиями, во многом схожий с PATH. GNU CC просматривает директории, указанные таким образом, при поиске программ, если он не может найти программы используя GCC_EXEC_PREFIX. LIBRARY_PATH Значением LIBRARY_PATH является список директорий, разделенных двоеточиями, во многом схожий с PATH. Когда GNU CC сконфигурирован как родной компилятор, он просматривает директории, указанные таким образом, при поиске специальных файлов линкера, если он не может найти их используя GCC_EXEC_PREFIX. Линковка при использовании GNU CC также использует эти директории при поиске обычных библиотек для опции '-l' (но директории, указанные с '-L', идут первыми). C_INCLUDE_PATH CPLUS_INCLUDE_PATH OBJC_INCLUDE_PATH Эти переменные окружения относятся к конкретным языкам. Значением каждой переменной является список директорий, разделенных двоеточиями, - 62 - во многом схожий с PATH. Когда GNU CC ищет заголовочные файлы, он просматривает директории, перечисленные в переменной для языка, который вы используете, после директорий указанных с помощью '-I', но до стандартных директорий заголовочных файлов. DEPENDENCIES_OUTPUT Если эта переменная установлена, ее значение указывает как выводить зависимости для Make, основанные на заголовочных файлах, обработанных компилятором. Этот вывод во многом похож на вывод опции '-M' (см. раздел [Опции Препроцессора]), но он идет в отдельный файл и является добавлением к обычным результатам компиляции. Значение DEPENDENCIES_OUTPUT может содержать только имя файла, в этом случае Make-правила пишутся в этот файл, а целевое имя извлекается из имени исходного файла. Или же, значение может иметь форму 'файл цель', в этом случае правила пишутся в файл 'файл' с использованием 'цели' в качестве целевого имени. 2.15. Выполнение Protoize Программа protoize является необязательной частью GNU C. Вы можете использовать ее, чтобы добавить прототипы к программе, конвертируя, таким образом, программу к ANSI C в одном отношении. Сопутствующая программа unprotioze делает обратное: она удаляет типы аргументов из всех прототипов, которые она находит. Когда вы запускете эту программу, вы должны указать набор исходных файлов в качестве аргументов командной строки. Программа конверсии начинает работу с того, что компилирует эти файлы, чтобы узнать, какие функции в них определены. Информация, собранная о файле foo, сохраняется в файле с именем 'foo.X'. После сканирования идет реальное преобразование. Указанные файлы все могут быть преобразованы; любые файлы, которые они включают (исходные или только заголовочные) также могут быть преобразованы. Но не все подходящие файлы преобразуются. По умолчанию, protoize и unprotoize преобразуют только исходные файлы и файлы заголовка в - 63 - текущем каталоге. Вы можете указать дополнительные каталоги, файлы в которых должны преобразовываться, с помощью опции `-dдиректория'. Вы можете также указать отдельные исключаемые файлы с помощью опции '-xфайл'. Файл преобразуется, если он подходит, имя его каталога соответствует одному из указанных имен каталогов, и его имя в каталоге не исключалось. Основное преобразование protoize состоит в переписывании большинства описаний функций и обьявлений функций, чтобы указывать типы параметров. Не перезаписываются только объявления и описания функций с переменным числом аргументов. protoize по выбору вставляет обьявления прототипов в начало исходного файла, делая их доступными для любых вызовов, которые предшествуют описанию функции. Или она может вставлять обьявления прототипов с блочной областью действия в блоках, где вызывается необъявленная функция. Основное преобразование unprotoize состоит в переписывании большинства обьявлений функций так, чтобы удалить типы параметров, и в приведении описаний функций к форме старого pre-ANSI стиля. Обе программы преобразования печатают предупреждение для любого обьявления или описания функции, которое они не могут преобразовать. Вы можете подавить эти предупреждения с помощью `-q'. Вывод protoize или unprotoize заменяет первоначальный исходный файл. Первоначальный файл переименовывается к имени, оканчивающемуся на `.save'. Если `.save' файл уже существует, исходный файл просто отбрасывается. И protoize, и unprotoize требуется сам GNU CC, чтобы просматривать программу и собирать информацию о функциях, которые она использует. Так что, ни одна из этих программ не будет работать до инсталяции GNU CC. Ниже расположена таблица опций, которые вы можете использовать с protoize и unprotoize. Каждая опция работает с обеими программами, - 64 - если не сказано иначе. -Bдиректория Ищет файл `SYSCALLS.c.X' в 'директории', вместо обычного каталога (обычно `/usr/local/lib'). Этот файл содержит информацию информацию о прототипах стандартных системных функций. Эта опция применима только к protoize. -cопции-компиляции Использует 'опции-компиляции' в качастве опций при запуске gcc для порождения `.X' файлов. Специальная опция `-aux-info' всегда передается в добавление, говоря gcc записать `.X' файл. Обратите внимание, что опции компиляции нужно передавать как одиночный параметр protoize или unprotoize. Если вы хотите указать несколько опций gcc, вы должны заключить весь набор опций трансляции в кавычки, чтобы сделать их одним словом для shell'а. Есть определенные параметры gcc, которые вы не можете использовать, потому что они производят неправильный вид вывода. Они включают `-g', `-O', `-c', `-S' и `-o'. Если вы включаете их в 'опции-компиляции', они игнорируются. -C Переименовывает файлы так, чтобы они заканчивались на `.C' вместо `.c'. Это удобно, если вы преобразуете C программу в C++. Эта опция применима только к protoize. -g Добавляет явные глобальные обьявления. Это значит вставлять явные обьявления в начало каждого исходного файла для каждой функции, которая вызывается в нем, и не была обьявлена. Эти обьявления предшествуют первому описанию функции, которое содержит вызов к необъявленной функции. Эта опция применима только к protoize. -iстрока Выравнивает обьявления параметров в старом стиле с помощью строки 'строка'. Эта опция применима только к unprotoize. - 65 - unprotoize преобразовывает описание-прототип функции в описание функции старого стиля, где параметры обьявлены между списоком параметров и начальным `{'. По умолчанию, unprotoize использует пять пробелов в качестве выравнивания. Если вы хотите вместо этого выравнивать только с одним пробелом, используйте `-i " "'. -k Сохраняет `.X' файлы. Обычно, они удаляются после завершения преобразования. -l Добавляет явные локальные обьявления. protoize с опцией `-l' вставляет обьявления-прототипы для каждой функции в каждом блоке, который вызывает функцию без обьявления. Эта опция применима только к protoize. -n Не делает никаких реальных изменений. Этот режим только печатает информацию о преобразованиях, которые были бы выполнены без `-n'. -N Не делает `.save' файлов. Первоначальные файлы просто удаляются. Используйте эту опцию с осторожностью. -p программа Использует программу 'программа' как компилятор. Нормально, используется имя `gcc'. -q Работает молча. Большинство предупреждений подавляется. -v Печатает номер версии, точно так же как `-v' для gcc. Если вам нужны специальные опции компилятора, чтобы компилировать один из исходных файлов вашей программы, тогда вы должны сгенерировать соответствующий `.X' файл особенно, при выполнении gcc на этом - 66 - исходном файле с соответствующими опциями и опцией `-aux-info'. Затем выполните protoize на всем наборе файлов. protoize будет использовать существующий `.X' файл, потому что он более новый, чем исходный файл. Например: gcc -Dfoo=bar file1.c -aux-info protoize * .c Вы должны включать специальные файлы наряду с остальными в команде protoize, даже если их `.X' файлы уже существуют, потому что иначе они не будут преобразовываться. См. раздел [Предостережения Protoize], для большей информации о том, как успешно использовать prtoize. . - 67 - 3. Установка GNU CC Ниже изложена процедура установки GNU CC в системе Unix. См. раздел [Установка на VMS], для VMS систем. В этом разделе мы считаем, что вы компилируете в том же каталоге, который содержит исходные фай- лы; см. раздел [Другие Директории], чтобы выяснить, как компилировать в отдельном каталоге в системе Unix. Вы не можете установить GNU C с помощью его самого в MS-DOS. Он не будет компилироваться никаким компилятором MS-DOS кроме его самого. Вы должны получить полный пакет компиляции DJGPP, который включает двоичные файлы, также как и исходные, и включает все необходимые инструментальные средства для компиляции и библиотеки. 1. Если вы предварительно построили GNU CC в том же самом каталоге для другой целевой машины, сделайте `make distclean', чтобы удалить все файлы, которые могут быть неправильными. Один из удаляемых файлов - `Makefile'; если `make distclean' говорит, что `Makefile' не существует, это, вероятно, означяет, что каталог уже правильно очищен. 2. В системе System V release 4, удостоверьтесь, что `/usr/bin' предшествует `/usr/ucb' в PATH. Команда 'cc' в `/usr/ucb' использует библиотеки, которые содержат ошибки. 3. Укажите хост, формирующую и целевую машинные конфигурации. Вы делаете это при выполнении файла `configure'. "Формирующая" машина - система которую вы используете, "хост" машина - система, где вы хотите выполнять получающийся в результате компилятор (обычно, формирующая машина), "целевая" машина - система, для которой вы хотите, чтобы компилятор генерировал код. Если вы строите компилятор, чтобы генерировать код для машины, на которой выполняется компилятор (родной компилятор), вы, обычно, не должны указывать никаких операндов configure; он попробует определить тип машины, на которой вы работаете, и использовать ее в качестве - 68 - формирующей, главной и целевой машин. Так что вы не должны указывать конфигурацию, когда строится родной компилятор, разве что configure не может определить вашу конфигурацию или определяет ее неправильно. В этих случаях, укажите имя конфигурации формирующей машины с опцией '--build', главная машина и адресат будут по умолчанию такими же как и формирующая машина. (Если вы строите кросскомпилятор, см. раздел [Построение и Установка Кросскомпилятора].) Пример: ./configure --build=sparc-sun-sunos4.1 Имя конфигурации может быть каноническим или более или менее сокращенным. Каноническиое имя конфигурации имеет три части, разделяемые черточкой. Оно выглядит следующим образом: `процессор-компания-система'. (Три части могут сами содержать черточки, `configure' может выяснить, какие черточки служат каким целям.) Например, `m68k-sun-sunos4.1' определяет Sun 3. Вы можете также заменять части конфигурации на прозвища или псевдонимы. Например, `sun3' заменяет `m68k-sun', так `sun3-sunos4.1' - другой способ указать Sun 3. Вы можете также использовать просто `sun3-sunos', так как версия SunOS считается по умолчанию равной четырем. `sun3-bsd' также работает, так как `configure' знает, что единственный вариант BSD на Sun 3 - SunOS. Вы можете указать номер версии после любого из типов систем и после некоторых из типов процессоров. В большинстве случаев, версия неуместна и игнорируется. Так что вы могли к тому же указать версию, если вы знаете ее. См. раздел [Конфигурации], за списком поддерживаемых имен конфи- гураций и примечаний относительно многих из них. Вы должны посмотреть примечания в этом разделе перед выполнением любых дальнейших действий - 69 - по установке GNU CC. Имеются четыре дополнительные опции, которые вы можете указать независимо, чтобы определить варианты аппаратных и программных конфигураций. Это - `--with-gnu-as', `--with-gnu-ld', `--with-stabs' и `--nfp'. '--with-gnu-as' Если вы будете использовать GNU CC с ассемблером GNU (GAS), вы должны указать это с помощью `--c-gnu-as' опцией когда вы запускаете `configure'. Использование этой опции не устанавливает GAS. Она только изменяет вывод GNU CC, чтобы он работал с GAS. Построение и установка GAS - это ваша задача. Наоборот, если вы не хотите использовать GAS и не определяете `--with-gnu-as' при построении GNU CC, это ваша задача - удостовериться, что GAS не установлен. GNU CC ищет программу с именем as в различных каталогах; если программа, которую он находит - GAS, тогда он запускает GAS. Если вы не уверены, где GNU CC находит ассемблер, который он использует, попробуйте указать `-v', когда вы его запускаете. Системы где важно, используете ли вы GAS: `hppa1.0-любая-любая', `hppa1.1-любая-любая', `i386-любая-sysv', `i386-любая-isc', `i860-любая-bsd', `m68k-bull-sysv', `m68k-hp-hpux', `m68k-sony-bsd', `m68k-altos-sysv', `m68000-hp-hpux', `m68000-att-sysv', `ANY-lynx-lynxos' и `mips-любая'). В любой другой системе `--with-gnu-as' не имеет никакого эффекта. В системах перечисленных выше (кроме HP-PA, ISC на 386 и `mips-sgi-irix5.*'), если вы используете GAS, вы должны, также, использовать GNU линкер (и указывать `--with-gnu-ld'). '--with-gnu-ld' Укажите опцию `--with-gnu-ld', если вы планируете использовать GNU линкер с GNU CC. - 70 - Эта опция не заставляет устанавливать GNU линкер; она только изменяет поведение GNU CC, чтобы он работал с GNU линкером. В частности, она запрещает установку `collect2' - программы, которая в противном случае служит в качестве внешнего интерфейса для линкера системы в большинстве конфигураций. `--with-stabs' В системах, основанных на MIPS, и в системах на Alpha вы должны определять, должен ли GNU CC создавать нормальный отладочный формат ECOFF или использовать stab'ы BSD-стиля, передаваемые через символьную таблицу ECOFF. Нормальный отладочный формат ECOFF не может полностью обрабатывать языки, отличные от C. Формат BSD stab'ов может обрабатывать другие языки, но он работает только с отладчиком GNU - GDB. Обычно, GNU CC использует отладочный формат ECOFF по умолчанию; если вы предпочитаете BSD stab'ы, укажите `--with-stabs', когда вы конфигурируете GNU CC. Вне зависимости от того, какое умолчание вы выбираете, когда конфигурируете GNU CC, пользователь может использовать опции `-gcoff' и `-gstabs+', чтобы явно указывать отладочный формат для конкретной компиляции. `--with-stabs' имеет значение также в системе ISC на 386, если используется '--with-gas'. Она включает применение отладочной информация stab'ов, встроенной в вывод COFF'а. Этот вид отладочной информации хорошо поддерживает C++; обычная отладочная информация COFF'а не делает этого. `--with-stabs' имеет значение также в системах на 386, исполняющих SVR4. Она включает применение отладочной информация stab'ов, встроенной в вывод ELF'а. C++ компилятор в настоящее время (2.6.0) не поддерживает отладочную информацию DWARF, обычно используемую на 386 SVR4 платформах; stab'ы обеспечивают работающий вариант. Он требует gas и gdb, поскольку обычные инструментальные средства SVR4 не могут генерировать или интерпретировать stab'ы. - 71 - `--nfp' В некоторых системах вы должны указывать, имеет ли машина модуль плавающей точки. Эти системы включают `m68k-sun-sunos n' и `m68k-isi-bsd'. В любой другой системе, `--nfp' в настоящее время не имеет никакого эффекта, хотя, возможно, имеются другие системы, где она могла бы быть полезна. `configure' просматривает подкаталоги исходного каталога в поисках других компиляторов, которые должны интегрироваться в GNU CC. GNU компилятор для C++, называемый G++ находится в подкаталоге с именем `cp'. `configure' вставляет правила в `Makefile' для построения всех этих компиляторов. Далее мы перечисляем файлы, которые будут устанавливаться `configure'. Обычно вы не должны иметь дело с этими файлами. * Создается файл с именем `config.h', который содержит директиву `#include' файла конфигурации верхнего уровня для машины, на которой вы будете запускать компилятор (см. Главу 18 [Конфигурация]). Этот файл отвечает за определение информации о хост-машине. Он включает `tm.h'. Файл конфигурации верхнего уровня размещается в подкаталоге `config'. Его имя - всегда `xm-что-нибудь.h'; обычно `xm-машина.h', но имеется некоторое количество исключений. Если ваша система не поддерживает символические ссылки, вы можете захотеть заставить `config.h' содержать директиву `#include', которая относится к соответствующему файлу. * Создается файл с именем `tconfig.h`, который включает конфигурационный файл верхнего уровня для вашей целевой машины. Это используется для компиляции некоторых программ для выполнения на этой машине. * Создается файл с именем `tm.h`, который включает макрофайл описания архитектуры для вашей целевой машины. Он должен быть в - 72 - поддиректории `config` и его именем часто является `машина.h`. * Командный файл `configure` также конструирует файл `Makefile` с помощью добавления некоторого текста к файлу-шаблону `Makefile.in`. Дополнительный текст приходит из файлов в каталоге `config` с именами `t-целевая` и `x-хост`. Если эти файлы не существуют, то это означает, что ничего не должно добавляться для данной целевой или хост-машины. 4. Стандартным каталогом для инсталляции GNU CC является '/usr/local/lib'. Если вы хотите установить его файлы где-нибудь в другом месте, '--prefix=каталог' при запуске 'configure'. Здесь 'каталог' - имя каталога, используемого вместо '/usr/local' для всех целей кроме одной: каталог '/usr/local/include' используется для поиска заголовочных файлов, вне зависимости от того, где вы установили компилятор. Чтобы переменить это имя, используйте опцию --local-prefix, указанную ниже. 5. Укажите '--local-prefix=каталог', если вы хотите, чтобы компилятор использовал каталог 'каталог/include' для поиска локально установленных заголовочных файлов вместо '/usr/local/include'. Вы должны указать `--local-prefix=директория', только если ваша система имеет другое соглашение (не `/usr/local') для того, где помещать файлы, специфические для ситемы. Не указывайте `/usr' как `--local-prefix'! Каталог, который вы используете для `--local-prefix' не должен содержать ни один из стандартных заголовочных файлов системы. Если он содержал их, то некоторые программы не будут компилироваться (включая GNU Emacs, на некоторых целевых машинах), потому что это будет отменять и аннулировать исправления заголовочных файлов, сделанные fixincludes. 6. Удостоверьтесь, что генератор синтаксических анализаторов Bison установлен. (Это необязательно, если выходные файлы Bison `c-parse.c' и `cexp.c' являются более поздними чем `c-parse.y' и `cexp.y', и вы не планируете изменять `.y' файлы.) - 73 - Bison версии, более старой чем 8 сентября 1988 года, будет производить неправильный вывод для `c-parse.c'. 7. Если вы выбрали конфигурацию для GNU CC, которая требует других инструментальных средств GNU (типа GAS или линкера GNU) вместо стандартных инструментальных средств системы, установите требуемые инструментальные средства в формируемом каталоге под именами `as', `ld' или другими. Это будет давать возможность компилятору находить соответствующие инструментальные средства для трансляции программы `enquire'. В качестве альтернативы, вы можете производить последующую трансляцию, используя значение системной переменной PATH, такое, что необходимые инструментальные средства GNU стоят до стандартных инструментальных средств системы. 8. Постройте компилятор. Просто напечатайте `make LANGUAGES=c' в каталоге компилятора. `LANGUAGES=c' указывает, что должен компилироваться только компилятор C. Makefile обычно строит компиляторы для всех поддерживаемых языков; в настоящее время - C, C++ и Objective C. Однако, C - единственый язык, который точно будет работать, если вы строите его с помощью другого, не GNU C компилятора. Кроме того, при построении чего-нибудь другого кроме C на этой стадии - трата времени. В общем, вы можете указать языки, которые нужно строить, указывая параметр `LANGUAGES= "список"', где 'список' - одно или более слов из списка `c', `c++' и `objective-c'. Если вы имеете какие-либо дополнительные компиляторы GNU как подкаталоги исходного каталога GNU CC, вы можете также указать их имена в этом списке. Игнорируйте любые предупреждения, которые вы можете увидеть относительно "statement not reached" в `insn-emit.c' - они нормальны. Аналогично, предупреждения относительно "unknown escape sequence" нормальны в `genopinit.c' и, возможно, в некоторых других файлах. Аналогично, вы должны игнорировать предупреждения относительно "constant is so large that it is unsigned" в `insn-emit.c' и - 74 - `insn-recog.c' и предупреждение относительно сравнения, всегда равного нулю, в `enquire.o'. Любые другие ошибки компиляции могут представлять собой ошибки в переносе на вашу машину или операционную систему и должны быть исследованы и сообщены. 9. Если вы строите кросскомпилятор, остановитесь здесь. См. Раздел [Построение и Установка Кросскомпилятора]. 10. Переместите объектные и исполнимые файлы первой стадии в подкаталог с помощью команды: make stage1 Файлы перемещаются в подкаталог с именем `stage1'. После того, как установка будет закончена, вы можете захотеть удалить эти файлы с помощью команды `rm -r stage1'. 11. Если вы выбрали конфигурацию для GNU CC, которая требует других инструментальных средств GNU (типа GAS или линкера GNU) вместо стандартных инструментальных средств системы, установите требуемые инструментальные средства в подкаталоге 'stage1' под именами `as', `ld' или другими. Это будет давать возможность стадии 1 компилятора находить соответствующие инструментальные средства на последующих стадиях. В качестве альтернативы, вы можете производить последующую трансляцию, используя значение системной переменной PATH, такое, что необходимые инструментальные средства GNU стоят до стандартных инструментальных средств системы. 12. Перекомпилируйте компилятор с помощью его самого командой: make CC="stage1/xgcc -Bstage1/" CFLAGS="-g -O2" Это называется созданием стадии 2 компилятора. 13. Если вы хотите проверить компилятор, компилируя его самим собой несколько раз, установите все остальные необходимые - 75 - инструментальные средства GNU (типа GAS или линкера GNU) в подкаталог `stage2', как вы делали в подкаталоге `stage1', затем сделайте так: make stage2 make CC="stage2/xgcc -Bstage2/" CFLAGS="-g -O2" Это называется созданием стадии 3 компилятора. Кроме опции `-B', опции компилятора должны быть такими же самыми, как когда вы строили стадию 2 компилятора. 14. Затем сравните последние объектные файлы с объектными файлами стадии 2 - они должны быть идентичными, кроме временных меток (если есть). Используйте эту команду для сравнения файлов: make compare Она будет упоминать любые объектные файлы, которые отличаются в стадиях 2 и 3. Любое различие, неважно насколько малое, указывает на то, что стадия 2 компилятора, компилировалась GNU CC неправильно, и следовательно существует потенциально серьезная ошибка, которую вы должны исследовать. 15. Установите управляющую программу компилятора, проходы компилятора и средства динамической поддержки с помощью `make install'. Используйте то же самое значение для `CC', `CFLAGS' и `LANGUAGES', которое вы использовали при компиляции файлов, которые устанавливались. Одна из причин, по которой это необходимо - то, что некоторые версии make имеют ошибки и перекомпилируют файлы, когда вы делаете этот шаг. Если вы используете те же самые значения переменных, то файлы будут перекомпилироваться правильно. Например, если вы построили стадию 2 компилятора, вы можете использовать следующую команду: make install CC="stage2/xgcc -Bstage2/" CFLAGS="-g -O" LANGUAGES="LIST" - 76 - Она копирует файлы `cc1', `cpp' и `libgcc.a' в файлы `cc1', `cpp' и `libgcc.a' в каталоге `/usr/local/lib/gcc-lib/цель/версия' - тот, в котором управляющая программа компилятора ищет их. Здесь 'цель' - тип целевой машины, указанный, когда вы выполняли `configure', а 'версия' - номер версии GNU CC. Она также копирует управляющую программу `xgcc' в `/usr/local/bin/gcc', так что она появляется в типичном маршруте поиска выполнения. 3.1. Конфигурации Поддерживаемые GNU CC Ниже перечислены возможный типы центральных процессоров: 1750a, a29k, alpha, arm, cN, clipper, dsp16xx, elxsi, h8300, hppa1.0, hppa1.1, i370, i386, i486, i586, i860, i960, m68000, m68k, m88k, mips, mipsel, mips64, mips64el, ns32k, powerpc, powerpcle, pyramid, romp, rs6000, sh, sparc, sparclite, sparc64, vax, we32k. Ниже перечислены распознаваемые имена компаний. Как вы можете видеть, обычные сокращения используются чаще, чем более длинные официальные имена. acorn, alliant, altos, apollo, att, bull, cbm, convergent, convex, crds, dec, dg, dolphin, elxsi, encore, harris, hitachi, hp, ibm, intergraph, isi, mips, motorola, ncr, next, ns, omron, plexus, sequent, sgi, sony, sun, tti, unicom, wrs. Имя компании имеет значение только для разрешения неоднозначностей, когда остальной указанной информации недостаточно. Вы можете опускать его, записывая только `процессор-система', если оно не необходимо. Например, `vax-ultrix4.2' эквивалентно `vax-dec-ultrix4.2'. Ниже расположен список типов систем: 386bsd, aix, acis, amigados, aos, aout, bosx, bsd, clix, coff, - 77 - ctix, cxux, dgux, dynix, ebmon, ecoff, elf, esix, freebsd, hms, genix, gnu, gnu/linux, hiux, hpux, iris, irix, isc, luna, lynxos, mach, minix, msdos, mvs, netbsd, newsos, nindy, ns, osf, osfrose, ptx, riscix, riscos, rtu, sco, sim, solaris, sunos, sym, sysv, udi, ultrix, unicos, uniplus, unos, vms, vsta, vxworks, winnt, xenix. Вы можете опустить тип системы, тогда `configure' догадывается об операционной системе из процессора и компании. Вы можете добавлять номер версии к типу системы; это может или не может давать различие. Например, вы можете писать `bsd4.3' или `bsd4.4', чтобы отличать версии BSD. Практически, номер версии наиболее необходим для `sysv3' и `sysv4', которые часто обрабатываются по-разному. Если вы определяете невозможную комбинацию типа `i860-dg-vms', вы можете получить сообщение об ошибке от `configure', или же он может игнорировать часть информации и сделать лучшее, что возможно с остальным. `configure' всегда печатает каноническиое имя для варианта, который он использовал. GNU CC не обеспечивает все возможные варианты. Часто индивидуальная модель машины имеет имя. Многие машинные имена распознаются как псевдонимы для комбинаций процессор/компания. Имеется таблица известных машинных имен: 3300, 3b1, 3bN, 7300, altos3068, altos, apollo68, att-7300, balance, convex-cN, crds, decstation-3100, decstation, delta, encore, fx2800, gmicro, hp7NN, hp8NN, hp9k2NN, hp9k3NN, hp9k7NN, hp9k8NN, iris4d, iris, isi68, m3230, magnum, merlin, miniframe, mmax, news-3600, news800, news, next, pbd, pc532, pmax, powerpc, powerpcle, ps2, risc-news, rtpc, sun2, sun386i, sun386, sun3, sun4, symmetry, tower-32, tower. Не забудьте, что машинное имя определяет и тип центрального процессора, и имя компании. Если вы хотите устанавливать ваши собственные файлы конфигурации собственного производства, вы можете - 78 - использовать `local' как имя компании, чтобы обращаться к ним. Если вы используете конфигурацию `процессор-local', то имя конфигурации без префикса процессора используется, чтобы сстроить имя файла конфигурации. Таким образом, если вы указываете `m68k-local', конфигурация использует файлы `m68k.md', `local.h', `m68k.c', `xm-local.h', `t-local' и `x-local', все в каталоге `config/m68k'. 3.2. Компиляция в Отдельном Каталоге Если вы хотите построить объектные и выполнимые файлы в каталоге отличном, от содержащего исходные файлы, ниже указано, что вы должны делать по-другому: 1. Удостоверьтесь, что вы имеете версию Make, которая поддерживает возможность `VPATH'. (GNU Make поддерживает ее, как и версии Make на большинстве систем BSD.) 2. Если вы когда-либо выполняли `configure' в исходном каталоге, вы должны отменить конфигурацию. Сделайте это выполнением: make distclean 3. Перейдите в каталог, в котором вы хотите построить компилятор перед выполнением `configure': mkdir gcc-sun3 cd gcc-sun3 В системах, которые не поддерживают символические ссылки, этот каталог должен быть в той же файловой системе, что и каталог исходных текстов. 4. Укажите, где находить `configure', когда вы его выполняете: ../gcc/configure ... - 79 - Это также сообщает `configure', где находить исходники компилятора; `configure' берет каталог из имени файла, которое использовалось, чтобы вызывать его. Но если вы хотите быть уверенными, вы можете указать исходный каталог с помощью опции `--srcdir' : ../gcc/configure - srcdir= ../gcc другое опции Каталог, который вы указываете с `--srcdir' не обязан быть тем же каталогом, в котором находится `configure'. Теперь, вы можете запускать `make' в этом каталоге. Вы не должны повторять шаги конфигурации показанные выше, при обычном изменении исходных файлов. Вы должны, однако, выполнить `configure' снова, при изменении файлов конфигурации, если ваша система не поддерживает символические ссылки. 3.3. Построение и Установка Кросскомпилятора GNU CC может функционировать как кросскомпилятор для многих машин, но не для всех. * Кросскомпиляторы между машинами с различными форматами плавающей точки не все сделаны работающими. GNU CC теперь имеет эмулятор плавающей точки, с которым они могут работать, но каждое описание целевой машины должно быть модифицировано, чтобы воспользоваться этим преимуществом. * Кросскомпиляция между машинами с различными размерами слов несколько проблематична и иногда не работает. Поскольку GNU CC генерирует ассемблерный код, вы, вероятно, нуждаетесь в кроссассемблере, чтобы GNU CC мог выполняться, порождая объектные файлы. Если вы хотите линковать не на целевой машине, вы к тому же нуждаетесь в кросслинкере. Вы также нуждаетесь в заголовочных файлах и библиотеках, подходящих для целевой машины, которые вы можете - 80 - установить на хост-машине. 3.3.1. Шаги Кросскомпиляции Компиляция и выполнение программ с использованием кросскомпилятора включает несколько шагов: * Запуск кросскомпилятора на хост-машине для порождения ассемблерных файлов для целевой машины. Это требует заголовочных файлов для целевой машины. * Компиляция файлов, произведенных кросскомпилятором. Вы можете делать это или ассемблером на целевой машине, или кроссассемблером на хост-машине. * Линковка этих файлов, чтобы сделать выполнимый файл. Вы можете делать это также линкером на целевой машине, или кросслинкером на хост-машине. Какую бы машину вы не использовали, вы нуждаетесь в библиотеках и в определенных стартовых файлах (обычно `crt....o') для целевой машины. Наиболее удобно делать все эти шаги на одной и той же главной машине, поскольку вы можете делать это все одним вызовом GNU CC. Это требует подходящего кроссассемблера и кросслинкера. Для некоторых целевых машин GNU ассемблер и линкер доступны. 3.3.2. Конфигурирование Кросскомпилятора Чтобы построить GNU CC как кросскомпилятор, вы начинаете с выполнения `configure'. Используйте `--target=цель', чтобы указать тип целевой машины. Если `configure' оказался неспособен правильно идентифицировать систему, на которой вы его выполняете, также укажите '--build=строющая'. Например, ниже показано, как конфигурировать кросскомпилятор, который производит код для системы HP 68030 с системой BSD, которую `configure' может правильно идентифицировать: ./configure --target=m68k-hp-bsd4.3 - 81 - 3.3.3. Инструментальные Средства и Библиотеки для Кросскомпилятора Если у вас есть кроссассемблер и кросслинкер, вы должны их теперь установить. Поместите их в каталог `/usr/local/цель/bin'. Имеется таблица инструментальных средств, которые вы должны включить в этот каталог: `as' Это должен быть кроссассемблер. `ld' Это должен быть кросслинкер. `ar' Это должен быть кроссархиватор: программа, которая может управлять архивными файлами (библиотеками линкера) в формате целевой машины. `ranlib' Это должна быть программа для создания таблицы идентификаторов в архивном файле. Установка GNU CC будет находить эти программы в этом каталоге и копировать или компоновать их в соответствующие места для кросскомпилятора, чтобы он находил их позже при выполнении. Самый простой способ обеспечивать эти файлы состoит в том, чтобы построить пакет Binutils и GAS. Cконфигурируйте их с теми же самыми `--host' и '--target', которые вы используете для конфигурирования GNU CC, затем постройте и установите их. Они устанавливают свои выполнимые файлы автоматически в соответствующий каталог. Но они не поддерживают все целевые машины, которые поддерживает GNU CC. Если вы хотите установить библиотеки, чтобы использовать с кросскомпилятором, такие, как стандарная библиотека C, поместите их в каталог `/usr/local/цель/lib'; установка GNU CC копирует все файлы в этом подкаталоге в соответствующее место, чтобы GNU CC мог находить их - 82 - и линковать с ними. Ниже показан пример копирования некоторого количества библиотек с целевой машины: ftp целевая-машина lcd /usr/local/цель/lib cd /lib get libc.a cd /usr/lib get libg.a get libm.a quit Точный набор библиотек, в которых вы нуждаетесь, и их местоположения на целевой машине, очень зависят от операционной системы. Многие целевые машины требуют "файлы начала" типа `crt0.o' и `crtn.o', которые линкуются к каждому выполнимому файлу; они также должны помещаться в `/usr/local/цель/lib'. Могут иметься несколько вариантов для `crt0.o', для применения с профиляцией или другими опциями компиляции. Ниже показан пример копирования этих файлов с целевой машины: ftp целевая-машина lcd /usr/local/цель/lib prompt cd /lib mget *crt*.o cd /usr/lib mget *crt*.o quit 3.3.4. Реальное Построение Кросскомпилятора Сейчас вы можете продолжить также, как и при построении компилятора для одной машины построением stage 1. Если вы обеспечили какой-либо вариант 'libgcc1.a'. тогда компиляция будет прервана в точке, где нужен этот файл, - 83 - 3.4. Стандартные Директории Заголовочных Файлов GCC_INCLUDE_DIR означает одно и то же для родного и кросс- компиляторов. Это место, где GNU CC сохраняет свои личные заголовочные файлы, а также где GNU CC сохраняет фиксированные заголовочные файлы. Кросскомпилирующий GNU CC запускает fixincludes над заголовочными файлами в '$(tooldir)/include'. (Если заголовочные файлы кросскомпиляции должны быть зафиксированы, они должны быть установлены до построения GNU CC. Если заголовочные файлы кросскомпиляции уже доступны для ANSI C и GNU CC, ничего специально делать не нужно.) GPLUS_INCLUDE_DIR означает одно и то же для родного и кросс- компиляторов. Это место, где g++ смотрит в первую очередь, в поисках заголовочных файлов. libg++ устанавливает только машинонезависимые заголовочные файлы в этой директории. LOCAL_INCLUDE_DIR используется только для родного компилятора. Обычно это '/usr/local/include'. GNU CC просматривает эту директорию, так что пользователи могут устанавливать заголовочные файлы в '/usr/local/include'. CROSS_INCLUDE_DIR используется только для кросскомпилятора. GNU CC ничего здесь не устанавливает. TOOL_INCLUDE_DIR используется как для родного, так и для кросскомпиляторов. Это место для инсталляции заголовочных файлов для других пакетов, которое GNU CC будет использовать. Для кросскомпилятора это '/usr/include'. Когда вы строите кросскомпилятор fixincludes обрабатывает все заголовочные файлы в этом каталоге. . - 84 - 4. Расширения Семейства Языка C GNU C обеспечивает некоторые языковые свойства, отсутствующие в стандарте ANSI C. (Опция `-pedantic` указывает GNU CC печатать предупреждающее сообщение, если какое-нибудь из этих свойств используется.) Чтобы проверить доступность этих свойств в условной компиляции, проверьте предопределенный макрос __GNUC__, который всегда определен под GNU CC. Эти расширения доступны в C и в Objective C. Большая часть из них также доступна в C++. 4.1. Операторы и Объявления в Выражениях Составной оператор, заключенный в скобки, может появляться в качестве выражения в GNU C. Это позволяет вам использовать циклы, операторы выбора и локальные переменные внутри выражения. Напомним, что составной оператор - это последовательность операторов, заключенная в фигурные скобки; в этой конструкции скобки окружают фигурные скобки. Например: ({ int y = foo (); int z; if (y > 0) z = y; else z = - y; z; }) является правильным (хотя и несколько более сложным чем необходимо) выражением для абсолютной величины foo(). Последней вещью в составном операторе должно быть выражение, после которого следует точка с запятой; значение этого подвыражения служит значением всей конструкции. (Если вы используете какой-нибудь другой вид оператора последним внутри фигурных скобок, конструкция - 85 - имеет тип void, и таким образом не имеет значения.) Это свойство особенно полезно, чтобы делать макроопределения "надежными" (такими, что они вычисляют каждый операнд ровно один раз.) Например, функция "максимум" обычно определяется как макро в стандартном C так: #define max(a,b) ((a) > (b) ? (a) : (b)) Но это определение вычисляет либо a, либо b дважды, с неправильными результатами, если операнд имеет побочные эффекты. В GNU C, если вы знаете тип операндов (здесь положим его int), вы можете безопасно определить макро таким образом: #define maxint(a,b) \ ({int _a = (a), _b = (b); _a > _b ? _a : _b; }) Встроенные операторы недопустимы в константых выражениях, таких как значения перечислимых констант, ширина битового поля или начальное значение статической переменной. Если вы не знаете тип операнда, вы все-таки можете сделать это, но вы должны использовать typeof (см. Раздел [Typeof]) или именование типов (см. Раздел [Именование Типов]). 4.2. Локально Объявляемые Метки Каждое выражение-оператор является областью, в которой могут быть объявлены локальные метки. Локальная метка - это просто идентификатор; вы можете делать переход на нее с помощью обычного оператора goto, но только изнутри выражения-оператора, к которому она принадлежит. Объявление локальной метки выглядит так: __label__ метка; или - 86 - __label__ метка1, метка2, ...; Объвления локальных меток должны идти в начале выражения-оператора сразу после `({` до любого обычного объявления. Объвление метки определяет имя метки, но не определяет саму метку. Вы должны сделать это обычным способом с помощью `метка:`, внутри выражения-оператора. Локальные метки полезны, так как выражения-операторы часто используются в макросах. Если макрос содержит вложенные циклы, goto может быть полезен для выхода из них. Однако обычная метка, чьей областью действия является вся функция, не может быть использована: если макрос может быть использован несколько раз в одной функции, метка будет определена в этой функции многократно. Локальная метка избегает этой проблемы. Например: #define SEARCH(array, target) \ ({ \ __label__ found; \ typeof (target) _SEARCH_target = (target); \ typeof (*(array)) *_SEARCH_array = (array); \ int i, j; \ int value; \ for (i = 0; i < max; i++) \ for (j = 0; j < max; j++) \ if (_SEARCH_array[i][j] == _SEARCH_target) \ - 87 - { value = i; goto found; } \ value = -1; \ found: \ value; \ }) 4.3. Метки как Значения Вы можете взять адрес метки, определенный в текущей функции (или объемлющей функции) с помощью унарной операции `&&`. Значение имеет тип void *. Это значение является константой и может быть использовано везде, где допускается константа этого типа. Например: void *ptr; ... ptr = &&foo; Чтобы использовать эти значения, вам нужно делать на них переход. Это делается с помощью вычисляемого оператора goto, `goto *выражение; `. Например: goto *ptr; Допустимо любое выражение типа void *. Один из способов использования этих констант заключается в инициализации статического массива, который будет служить таблицей переходов: static void *array[] = { &&foo, &&bar, &&hack }; Затем вы можете выбрать метку с помощью индексации таким образом: goto *array[i]; - 88 - Заметим, что здесь не проверяется, находится ли индекс в допустимых границах - индексация массивов в C никогда не делает этого. Такой массив значений меток служит цели во многом подобной цели оператора switch. Оператор switch является более понятным, так что используйте его вместо массива, если только задача не является неподходящей для оператора switch. Другим использованием значений меток является интерпретатор шитого кода. Метки внутри функции интерпретатора могут быть записаны в шитый код для супербыстрой обработки. Вы можете использовать этот механизм для перехода на код в различных функциях. Если вы так делаете, могут произойти совершенно непредсказуемые вещи. Лучший способ избежать этого - сохранять адреса меток только в автоматических переменных и никогда не передавать их в качестве параметров. 4.4. Вложенные Функции Вложенная функция - это функция, определенная внутри другой функции. Имя вложенной функции является локальным в блоке, где она определена. Например, здесь мы определяем вложенную функцию с именем square и вызываем ее дважды: foo (double a, double b) { double square (double z) { return z * z; } return square (a) + square (b); } Вложенная функция имеет доступ ко всем переменным объемлющей функции, которые видны в точке ее определения. Это называется "лексическая область действия". Например, ниже мы показываем вложенную функцию, которая использует наследуемую переменную с именем offset: - 89 - bar (int *array, int offset, int size) { int access (int *array, int index) { return array[index + offset]; } int i; ... for (i = 0; i < size; i++) ... access (array, i) ... } Определения вложенных функций разрешаются внутри функций, где допустимы определения переменных; то есть в любом блоке перед первым оператором в блоке. Можно вызвать вложенную функцию из точки вне области действия ее имени, сохранив ее адрес или передав адрес в другую функцию: hack (int *array, int size) { void store (int index, int value) { array[index] = value; } intermediate (store, size); } Здесь функция intermediate получает адрес функции store в качестве параметра. Если intermediate вызывает store, аргумент, передаваемый в store, используется для записи в array. Но эта техника работает только до тех пор, пока объемлющая функция (в этом примере - 90 - hack) не возвратит управление. Если вы пытаетесь вызвать вложенную функцию с помощью ее адреса после того, как объемлющая функция возвратила управление, все полетит к чертям. Если вы пытаетесь вызвать ее после того, как объемлющая область действия закончила работу, и если она ссылается на одну из переменных, которые больше не лежат в области действия, может вам и повезет, но неразумно рисковать. Однако, если вложенная функция не ссылается ни на что, вышедшее из области действия, вы в безопасности. GNU CC выполняет взятие адреса вложенной функции, используя технику, называемую "trampolines". Бумага, описывающая ее, доступна из 'maya.idiap.ch' в директории 'pub/tmb' в файле 'usenix88-lexic.ps.Z'. Вложенная функция может делать переход на метку, наследуемую от объемлющей функции, если метка явно объявлена в объемлющей функции (см. Раздел [Локальные Метки]). Такой переход немедленно возвращает в объемлющую функцию, покидая вложенную функцию, которая сделала goto, а также любые промежуточные функции. Пример: bar (int *array, int offset, int size) { __label__ failure; int access (int *array, int index) { if (index > size) goto failure; return array[index + offset]; } int i; ... for (i = 0; i < size; i++) ... access (array, i) ... ... return 0; /* Управление попадает сюда из access, если обнаруживается ошибка. */ - 91 - failure: return -1; } Вложенная функция всегда имеет внутреннее связывание. Объявление ее с extern является ошибочным. Если вам нужно объявить вложенную функцию до ее определения, используйте auto (который в противном случае бесполезен для объявлений функций). bar (int *array, int offset, int size) { __label__ failure; auto int access (int *, int); ... int access (int *array, int index) { if (index > size) goto failure; return array[index + offset]; } ... } 4.5. Конструирование Вызовов Функций - 92 - Используя встроенные функции, описанные ниже, вы можете записать полученные аргументы функции и вызвать другую функцию с теми же аргументами, не зная количество и типы аргументов. Вы можете также записать возвращаемое значение этого вызова функции и позже вернуть это значение, не зная, какой тип данных функция пыталась вернуть (если вызывавшая функция ожидает этот тип данных). __builtin_apply_args () Эта встроенная функция возвращает указатель типа void * на данные, описывающие, как выполнять вызов с теми же аргументами, которые были переданы текущей функции. Функция сохраняет регистр указателя аргументов, адреса структурного значения и все регистры, которые могут быть использованы для передачи аргументов в функцию в блок памяти, выделяемый на стеке. Затем она возвращает адрес этого блока. __builtin_apply (функция, аргументы, размер) Эта встроенная функция вызывает 'функцию' (типа void (*)()) с копированием параметров, описываемых 'аргументами' (типа void *) и 'размером' (типа int). Значение 'аргументы' должно быть значением, которое возвращено __builtin_apply_args (). Аргумент 'размер' указывает размер стековых данных параметров в байтах. Эта функция возвращает указатель типа void * на данные, описывающие, как возвращать какое-либо значение, которое вернула 'функция'. Данные сохраняются в блоке памяти, выделенном на стеке. Не всегда просто вычислить подходящее значение для 'размера'. Это значение используется __builtin_apply () для вычисления количества данных, которые должны быть положены на стек и скопированы из области входных аргументов. __builtin_return (результат) - 93 - Эта встроенная функция возвращает значение, описанное 'результатом' из объемлющей функции. Вы должны указать в качестве 'результата' значение, возвращенное __builtin_apply (). 4.6. Именование Типа Выражения Вы можете дать имя типу выражения, используя объявление typedef с инициализатором. Ниже показано, как определить имя как имя типа выражения: typedef имя = выражение; Это полезно в соединении с возможностью выражений-операторов. Ниже показано, как эти две возможности могут бвть использованы, чтобы определить безопасный макрос "максимум", который оперирует с любым арифметическим типом: #define max(a,b) \ ({typedef _ta = (a), _tb = (b); \ _ta _a = (a); _tb _b = (b); \ _a > _b ? _a : _b; }) Смысл использования имен, которые начинаются с подчеркиваний для локальных переменных в том, чтобы избегать конфликтов с именами переменных, которые встречаются в выражениях, которые подставляются вместо a и b. В конечном итоге, мы надеемся разработать новую форму синтаксиса объявлений, которая позволит объявлять переменные, чьи области действия начинаются только после их инициализаторов; это будет более надежным способом предотвращения подобных конфликтов. 4.7. Ссылки на Тип с Помощью typeof Другой способ сослаться на тип выражения - с помощью typeof. Синтаксис использования этого ключевого слова - такой же как и у sizeof, но семантически конструкция действует подобно имени типа, определенного с помощью typedef. Есть два способа записи аргумента typeof: с выражением и с типом. - 94 - Ниже показан пример с выражением: typeof (x[0](1)) Здесь предполагается, что x является массивом функций; описанный тип является типом значений этих функций. Ниже показан пример с именем типа в качестве аргумента: typeof (int *) Здесь описанный тип является типом указателей на int. Если вы пишете заголовочный файл, который должен работать при включении в ANSI C программы, пишите __typeof__ вместо typeof. См. Раздел [Альтернативные Ключевые Слова]. Конструкция typeof может использоваться везде, где допустимо typedef-имя. Например, вы можете использовать ее в объявлении, в приведении или внутри sizeof или typeof. 4.8. Обобщенные L-значения Составные выражения, условные выражения и приведения позволяются в качестве L-значений, при условии, что их операнды являются L-значениями. Это означает, что вы можете брать их адреса или сохранять в них значения. Например, составному выражению может быть присвоено что-либо, при условии, что последнее выражение в последовательности является L-значением. Эти два выражения являются эквивалентными: (a, b) += 5 a, (b += 5) Таким же образом, может быть взят адрес составного выражения. Эти два выражения являются эквивалентными: - 95 - &(a, b) a, &b Условное выражение является допустимым L-значением, если его типом не является void, и при этом обе его ветви являются допустимыми L-значениями. К примеру, эти два выражения являются эквивалентными: (a ? b : c) = 5 (a ? b = 5 : (c = 5)) Приведение является допустимым L-значением, если его операнд является L-значением. Простое присваивание, чьей левой частью является приведение, работает, сначала преобразуя правую часть в указанный тип, а затем к типу внутренней части выражения левой части. После того, как это значение записывается, значение преобразуется обратно к указанному типу, чтобы получить значение присваивания. Таким образом, если a имеет тип char *, следующие два выражения являются эквивалентными: (int)a = 5 (int)(a = (char *)(int)5) Присваивание с арифметической операцией, такое как '+=', примененное к приведению, выполняет арифметическую операцию, используя тип, получающийся из приведения, и затем продолжает как и в предыдущем случае. Следовательно, эти два выражения являются эквивалентными: (int)a += 5 (int)(a = (char *)(int) ((int)a + 5)) Вы не можете взять адрес L-значения приведения, потому что использование его адреса не могло бы выполняться согласованно. 4.9. Условные Выражения с Опущенными Операндами Средний операнд в условном выражении может быть опущен. Тогда, если первый операнд не равен нулю, его значение является значением условного выражения. - 96 - Следовательно, выражение x ? : y имеет значение x, если оно не равно нулю, в противном случае - значение y. Этот пример полностью эквивалентен x ? x : y В этом простом случае, возможность опускать средний операнд не особенно полезна. Она становится полезной, когда первый операнд содержит, или может содержать (если это макроаргумент) побочные эффекты. В этом случае повторение операнда в середине может выполнить побочный эффект дважды. Опускание среднего операнда использует уже вычисленное значение без нежелательных эффектов его перевычисления. 4.10. Двухсловные Целые GNU C поддерживает типы данных для целых, которые вдвое длиннее long int. Просто пишите long long int для знакового целого, или unsigned long long int для беззнакового целого. Чтобы сделать целую константу типа long long int, добавьте суффикс LL к целому. Чтобы сделать целую константу типа unsigned long long int, добавьте суффикс ULL к целому. 4.11. Комплексные Числа GNU C поддерживает комплексные типы данных. Вы можете объявить как комплексные целые типы, так и комплексные плавающие типы, используя ключевое слово __complex__ . Например, '__complex__ double x;' объявляет x как переменную, чьи вещественная и мнимая части имеют тип double; '__complex__ short int y;' объявляет y, имеющей вещественную и мнимую части типа short int. Чтобы записать константу комплексного типа данных, используйте - 97 - суффикс i или j (любой из них - они эквивалентны). Например, 2.5fi имеет тип __complex__ float, а 3i имеет тип __complex__ int. Такие константы всегда имеют чисто мнимое значение, но вы можете сформировать любое комплексное значение с помощью добавления вещественной константы. Чтобы извлечь вещественную часть комплеснозначного выражения, пишите '__real__ выражение'. Аналогично, используйте __imag__ для извлечения мнимой части. Операция '~' выполняет комплексное сопряжение, когда используется над значением комплексного типа. 4.12. Массивы Нулевой Длины Массивы нулевой длины разрешаются в GNU C. Они являются очень полезными в качестве последнего элемента структуры, который в действительности является заголовком объекта переменной длины: struct line { int length; char contents[0]; }; { struct line *thisline = (struct line *) malloc (sizeof (struct line) + this_length); thisline->length = this_length; } В стандартном C вы должны бы были дать contents длину 1, который означает, что вы либо должны терять память, либо усложнять аргумент malloc. 4.13. Массивы Переменной Длины Автоматические массивы переменной длины допустимы в GNU C. Эти массивы объявляются подобно любым другим автоматическим массивам, но с - 98 - длиной, которая не является константным выражением. Память выделяется в точке объявления и освобождается при выходе из блока. Например: FILE * concat_fopen (char *s1, char *s2, char *mode) { char str[strlen (s1) + strlen (s2) + 1]; strcpy (str, s1); strcat (str, s2); return fopen (str, mode); } Переход вне области действия массива освобождает память. Переход в область действия недопустим. Вы можете использовать функцию alloca, чтобы получить эффект во многом подобный массивам переменной длины. Функция alloca допустима во многих других реализациях C (но не во всех). С другой стороны, массивы переменной длины являются более элегантными. Есть другие отличия между этими двумя методами. Место, выделяемое с помощью alloca, существует пока объемлющая функция не сделает возврат. Место для массива переменной длины освобождается, как только заканчивается область действия имени массива. (Если вы используете как массивы переменной длины, так и alloca в одной и той же функции, освобождение массива переменной длины так же освободит все выделенное после с помощью alloca.) Вы можете также использовать массивы переменной длины в качестве аргумента функции: struct entry tester (int len, char data[len][len]) { ... } Длина массива вычисляется один раз при выделении памяти и - 99 - вспоминается в области действия массива, если вы берете ее с помощью sizeof. Если вы хотите передать массив первым, а длину после, вы можете использовать предварительное объявление в списке параметров - другое расширение GNU. struct entry tester (int len; char data[len][len], int len) { ... } 'int len' перед точкой с запятой является предварительным объявлением параметра и служит тому, чтобы сделать имя len известным при разборе объявления data. Вы можете писать любое число таких предварительных объявлений параметров в списке параметров. Они могут разделяться запятыми или точками с запятыми, но последнее из них должно кончаться точкой с запятой, за которой следуют "реальные" объявления параметров. Каждое предварительное объявление должно соответствовать "реальному" объявлению в имени параметра и типе данных. 4.14. Макросы с Переменным Числом Аргументов В GNU C макрос может получать переменное число аргументов, во многом подобно тому, как и функция. Синтаксис определения макроса выглядит очень похожим на используемый для функций. Пример: #define eprintf(format, args...) \ fprintf (stderr, format , ## args) Здесь args - это остаточный аргумент: он принимает ноль или больше аргументов - столько, сколько содержит вызов. Все они вместе с запятыми между ними образуют значение args, которое подставляется в тело макроса там, где используется args. Таким образом, мы имеем следующее расширение: - 100 - eprintf ("%s:%d: ", input_file_name, line_number) ==> fprintf (stderr, "%s:%d: " , input_file_name, line_number) Заметим, что запятая после строковой константы идет из определения eprintf, в то время как последняя запятая идет из значения args. Смысл использования '##' в обработке случая, когда args не соответствует ни одного аргумента. В этом случае args имеет пустое значение. Тогда вторая запятая в определении становится помехой: если она прошла бы через расширение макроса, мы бы получили что-нибудь подобное: fprintf (stderr, "success!\n" , ) что является неправильным синтаксисом C. '##' освобождает от запятой, так что мы получаем следующее: fprintf (stderr, "success!\n") Это специальное свойство препроцессора GNU C: '##' перед остаточным аргументом, который пуст, отбрасывает предшествующую последовательность непробельных символов из макроопределения. 4.15. Массивы Не L-значения Могут Иметь Индексы Позволяется индексация массивов, которые не являются L-значениями, хотя даже унарная операция '&' не позволяется. Например, это является допустимым в GNU C, хотя и неверным в других диалектах C: struct foo {int a[4];}; struct foo f(); bar (int index) { return f().a[index]; - 101 - } 4.16. Арифметика над Указателями на void и на Функции В GNU C поддерживаются операции сложения и вычитания с указателями на void и на функции. Это делается, принимая размер void или функции равным 1. Следствием этого является то, что операция sizeof также позволяется над void и над типами функций и возвращает 1. Опция '-Wpointer-arith' требует предупреждения, если это расширение используется. 4.17. Неконстантные Инициализаторы Как в стандартном C++ элементы агрегатного инициализатора автоматической переменной не обязаны быть константными выражениями в GNU C. Ниже показан пример инициализатора с элементами, меняющимися во время выполнения: foo (float f, float g) { float beat_freqs[2] = { f-g, f+g }; ... } 4.18. Выражения Конструкторов GNU C поддерживает выражения конструкторов. Конструктор выглядит как приведение, содержащее инициализатор. Его значение является объектом типа, указанного в приведении, содержащее элементы, указанные в инициализаторе. Обычно указанный тип является структурой. Предположим, что struct foo и structure объявлены, как показано: struct foo {int a; char b[2];} structure; - 102 - Ниже показан пример конструирования struct foo с помощью конструктора: structure = ((struct foo) {x + y, 'a', 0}); Это эквивалентно написанному ниже: { struct foo temp = {x + y, 'a', 0}; structure = temp; } Вы можете также сконструировать массив. Если все элементы конструктора являются (или получаются из) простыми константными выражениями, подходящими для использования в инициализаторах, тогда конструктор является L-значением и может быть приведен к указателю на свой первый элемент, как показано ниже: char **foo = (char *[]) { "x", "y", "z" }; Конструкторы массива, чьи элементы не являются простыми константами, не очень полезны, потому что они не являются L-значениями. 4.19. Помеченные Элементы в Инициализаторах Стандартный C требует, чтобы элементы инициализатора появлялись в фиксированном порядке, в том же самом, в котором элементы массива или структуры инициализируются. В GNU C вы можете дать элементы в любом порядке, указывая индексы массива или имена полей структуры, к которым они применяются. Чтобы указать индекс массива, напишите '[индекс]' или '[индекс] =' перед значением элемента. Например, int a[6] = { [4] 29, [2] = 15 }; эквивалентно - 103 - int a[6] = { 0, 0, 15, 0, 29, 0 }; Значение индекса должно быть константным выражением, даже если инициализируемый массив является автоматическим. Чтобы инициализировать диапазон элементов одним и тем же значением, напишите '[первый ... последний] = значение'. Например: int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 }; Заметим, что длина массива равна максимальному указанному значению плюс 1. В инициализаторе структуры укажите имя инициализируемого поля с помощью 'имяструктуры:' перед значением элемента. Пусть, например, дана следующая структура: struct point { int x, y; }; следующая инициализация struct point p = { y: yvalue, x: xvalue }; эквивалентна struct point p = { xvalue, yvalue }; Другой синтаксис, который имеет то же значение: '.имяструктуры =', как показано ниже: struct point p = { .y = yvalue, .x = xvalue }; Вы также можете использовать метку элемента при инициализации объединения, чтобы указать, какой элемент объединения должен использоваться. Например, union foo { int i; double d; }; - 104 - union foo f = { d: 4 }; преобразует 4 в double, чтобы записать его в объединение, использую второй элемент. Напротив, приведение 4 к типу union foo сохранит его в объединении как целое i, поскольку оно целое. (См. Раздел [Приведение к Объединению].) Вы можете скомбинировать эту технику именования элементов с обычной C инициализацией последовательных элементов. Каждый элемент инициализатора, который не имеет метки, приеняется к следующему элементу массива или структуры. Например, int a[6] = { [1] = v1, v2, [4] = v4 }; эквивалентно int a[6] = { 0, v1, v2, 0, v4, 0 }; 4.20. Диапазоны Case Вы можете указать диапазон последовательных значений в одной метке case так: case LOW ... HIGH: Будьте внимательны: Пишите пробелы вокруг '...', в противном случае оно может быть разобрано неправильно. 4.21. Приведение к Типу Объединения Приведение к типу объединения подобно другим приведениям, за тем исключением, что указываемый тип является типом объединения. Вы можете указать тип либо с помощью union тег, либо с помощью typedef имени. Приведение к объединению является в действительности конструктором, а не приведением, и, следовательно, не дает L-значения, как нормальное приведение. (См. Раздел [Конструкторы].) - 105 - Типы, которые могут быть приведены к типу объединения, являются типами членов объединения. Таким образом, если даны следующие объединение и переменные: union foo { int i; double d; }; int x; double y; тогда и x, и y могут быть приведены к union foo. Использование приведения в правой части присваивания переменной типа объединения эквивалентно записи в член объединения: union foo u; ... u = (union foo) x == u.i = x u = (union foo) y == u.d = y Вы можете также использовать приведение к объединению в качестве аргумента функции: void hack (union foo); ... hack ((union foo) x); 4.22. Объявления Атрибутов Функций В GNU C вы можете объявить определенные вещи о функциях, вызываемых в вашей программе, которые помогают компилятору оптимизировать вызовы функций и более внимательно проверять ваш код. Ключевое слово __attribute__ позволяет вам указывать специальные атрибуты при создании объявлений. За этим ключеным словом следует описание атрибута в двойных скобках. В данный момент для функций определены восемь атрибутов: noreturn, const, format, section, constructor, destructor, unused и weak. Другие атрибуты, включая section, поддерживаются для объявлений переменных (см. Раздел [Атрибуты Переменных]) и для типов (см. Раздел [Атрибуты Типов]). - 106 - Вы можете указывать атрибуты с '__', окружающими каждое ключевое слово. Это позволяет вам использовать их в заголовочных файлах, не заботясь о том, что могут быть макросы с тем же именем. Например, вы можете использовать __noreturn__ вместо noreturn. noreturn Несколько стандартных библиотечных функций, таких как abort и exit не могут вернуть управление. GNU CC знает это автоматически. Некоторые программы определяют свои собственные функции, которые никогда не возвращают управление. Вы можете объявить их noreturn, чтобы сообщить компилятору этот факт. Например: void fatal () __attribute__ ((noreturn)); void fatal (...) { ... /* Печатает сообщение об ошибке. */ ... exit (1); } Ключевое слово noreturn указывает компилятору принять, что функция fatal не может возвратить управление. Тогда он может делать оптимизацию, несмотря на то, что бы случилось, если бы fatal вернула управление. Это делает код немного лучше. Более важно, что это помогает избегать ненужных предупреждений об инициализированных переменных. Атрибут noreturn не реализован в GNU C версии ранее чем 2.5. const Многие функции не используют никаких значений, кроме своих аргументов, и не имеют эффекта, кроме возвращаемого значения. Такая функция может быть объектом исключения общих подвыражений и оптимизации циклов аналогично арифметической операции. Такую функцию следует объявить с атрибутом const. Например, - 107 - int square (int) __attribute__ ((const)); говорит, что гипотетическую функцию square безопасно вызывать меньшее количество раз, чем сказано в программе. Атрибут const не реализован в GNU C версии ранее 2.5. Заметим, что функция, которая имеет параметром указатель и использует данные, на которые он указывает, не должна объявляться const. Аналогично, функция, которая вызывает не-const функцию, обычно не должна быть const. format (тип, строка-индекс, первый-проверяемый) Атрибут format указывает, что функция принимает аргументы в стиле printf или scanf, которые должны быть проверены на соответствие со строкой формата. Например, объявление extern int my_printf (void *my_object, const char *my_format, ...) __attribute__ ((format (printf, 2, 3))); заставляет компилятор проверять параметры в вызове my_printf на соответствие printf-стилю строки формата my_format. Параметр 'тип' определяет, как строка формата интерпретируется, и должен быть либо printf, либо scanf. Параметр 'строка-индекс' указывает, какой параметр является строкой формата (начиная с 1), а 'первый-проверяемый' является номером первого проверяемого аргумента. Для функций, у которых аргументы не могут быть проверены (таких как vprintf), укажите в качестве третьего параметра ноль. В этом случае компилятор только проверяет строку формата на корректность. Компилятор всегда проверяет формат для функций ANSI библиотеки printf, fprintf, sprintf, scanf, vprintf, vfprintf, vsprintf, когда такие предупреждения запрашиваются (используя '-Wformat'), так что нет нужды модифицировать заголовочный файл 'stdio.h'. section ("имя-секции") - 108 - Обычно, компилятор помещает генерируемый код в секцию text. Однако, иногда, вам нужны дополнительные секции или же вам нужно, чтобы определенные функции оказались в специальных секциях. Атрибут section указывает, что функция живет в определенной секции. Например, объявление extern void foobar (void) __attribute__ ((section ("bar"))); помещает функцию foobar в секцию bar. constructor destructor Атрибут constructor заставляет функцию вызываться автоматически перед выполнением main (). Аналогично, атрибут destructor заставляет функцию вызываться автоматически после того, как main () завершилась или вызвана exit (). Функции с этими атрибутами полезны для инициализации данных. unused Этот атрибут, примененный к функции, означает, что функция, возможно, может быть неиспользуемой. GNU CC не будет порождать предупреждение для этой функции. weak Атрибут weak приводит к тому, что объявление будет порождаться как слабый символ, а не глобальный. Это прежде всего полезно для определения бибилиотечных функций, которые могут быть переопределены пользовательским кодом, хотя это может быть использовано и с объявлениями не-функций. alias ("назначение") Атрибут alias заставляет породить объявление как синоним другого символа, который должен быть указан. Например, void __f () { /* делает что-либо */; } void f () __attribute__ ((weak, alias ("__f"))); объявляет 'f' слабым синонимом для '__f'. - 109 - 4.23. Прототипы и Определения Функций в Старом Стиле GNU C расширяет ANSI C, чтобы позволять прототипам функций перекрывать последующие определения старого стиля. Рассмотрим следующий пример: /* Использует прототипы, если компилятор не является старым. */ #if __STDC__ #define P(x) x #else #define P(x) () #endif /* Прототип объявления функции. */ int isroot P((uid_t)); /* Определение функции в старом стиле. */ int isroot (x) /* ??? потеря здесь ??? */ uid_t x; { return x == 0; } Предположим тип uid_t оказался short. ANSI C не допускает этот пример, потому что короткие аргументы в старом стиле определений расширяются. Следовательно, в этом примере аргумент определения функции в действительности int, который не соответствует типу аргумента прототипа short. 4.24. Комментарии в C++ Стиле В GNU C вы можете использовать комментарии C++ стиля, которые начинаются с '//' и продолжаются до конца строки. Многие другие реализации C позволяют такие комментарии, и они, вероятно, будут в будущем стандарте C. Однако, комментарии в C++ стиле не распознаются, если вы указываете '-ansi' или '-traditional', покольку они не - 110 - совместимы с традиционными конструкциями типа ???. 4.25. Знак Доллара в Идентификаторах В GNU C вы можете использовать знак доллара в идентификаторах. Это потому что многие традиционные реализации C позволяют такие идентификаторы. На некоторых машинах, знак доллара разрешается в идентификаторах, если вы указываете '-traditional'. В некоторых системах они разрешаются по умолчанию, даже если вы не используете '-traditional'. Но он никогда не позволяется, если вы указываете '-ansi'. 4.26. Символ ESC в Константах Вы можете использовать последовательность '\e' в строковой или символьной константе в качестве ASCII символа ESC. 4.27. Выравнивание Типов и Переменных Ключевое слово __alignof__ позволяет вам узнавать, как выравниваются объекты, или минимальным выравниванием, требуемым для типа. Его синтаксис - такой же как у sizeof. Например, если целевая машина требует, чтобы значение типа double выравнивалось на 8-байтную границу, тогда __alignof__ (double) равен 8. Это верно для большинства RISC машин. На более традиционных архитектурах __alignof__ (double) равен 4 или даже 2. Некоторые машины в действительности никогда не требуют выравнивания, они позволяют ссылки на любой тип данных, даже по нечетному адресу. Для этих машин __alignof__ выдает рекомендуемое выравнивание типа. Когда операндом __alignof__ является L-значение, а не тип, результатом является максимальное выравнивание, которое имеет L-значение. Оно может иметь это выравнивание из-за его типа данных, или потому что оно является частью структуры и наследует выравнивание - 111 - от этой структуры. Например, после этого объявления struct foo { int x; char y; } foo1; значение __alignof__ (foo1.y) равно, вероятно, 2 или 4 - такое же как __alignof__ (int) хотя тип данных foo1.y сам не требует выравнивания. Связанное с этим свойство, которое позволяет вам указывать выравнивание объекта - это __attribute__ ((aligned (выравнивание))), см. следующий раздел. 4.28. Указание Атрибутов Переменных Ключевое слово __attribute__ позволяет вам указывать специальные атрибуты переменных или полей структуры. За этим ключевым словом следует спецификация атрибута в двойных скобках. Восемь атрибутов поддерживаются в данный момент для переменных: aligned, mode, nocommon, packed, section, transparent_union, unused, weak. Другие атрибуты допустимы для функций (см. Раздел [Атрибуты Функций]) и для типов (см. Раздел [Атрибуты Типов]). Вы можете указывать атрибуты с '__', окружающими каждое ключевое слово. Это позволяет вам использовать их в заголовочных файлах, не заботясь о том, что могут быть макросы с тем же именем. Например, вы можете использовать __aligned__ вместо aligned. aligned (выравнивание) Этот атрибут определяет минимальное выравнивание для переменной или поля структуры, измеряемое в байтах. Например, объявление int x __attribute__ ((aligned (16))) = 0; заставляет компилятор размещать глобальную переменную x по 16-байтной границе. На 68040 это может быть использовано вместе с asm выражением, чтобы использовать инструкцию move16, которой требуются операнды, выравненные на 16 байт. Вы можете также указать выравнивание полей структуры. Например, - 112 - для создания пары int, выравненной на границу двойного слова, вы могли бы написать: struct foo { int x[2] __attribute__ ((aligned (8))); }; Это является альтернативой созданию объединения с double членом, который заставляет выравнивать объединение на границу двойного слова. Невозможно определять выравнивание функций, выравнивание функций определяется требованиями машины и не может быть изменено. Вы не можете указать выравнивание для typedef имени, потому что такое имя является только синонимом, а не отдельным типом. Как в предыдущих примерах, вы можете явно указать выравнивание (в байтах), которое вы хотели бы, чтобы использовал компилятор для данной переменной или поля структуры. В качестве альтернативы, вы можете оставить размер выравнивания и только попросить компилятор выравнивать переменную или поле по максимальному полезному выравниванию для целевой машины, для которой вы компилируете. Например, вы могли бы написать: short array[3] __attribute__ ((aligned)); Атрибут aligned может только увеличить выравнивание, но вы можете уменьшить его с помощью указания packed. См. ниже. Заметим, что эффективность атрибутов aligned может быть ограничена ограничениями вашего линкера. Во многих системах, линкер может только обрабатывать выравнивание переменных, не превышающее определенного предела. (Для некоторых линкеров максимальное поддерживаемое выравнивание может быть очень и очень малым.) См. документацию по вашему линкеру для дальнейшей информации. mode (вид) Этот атрибут указывает тип данных для объявления - тип, который соответствует виду 'вид'. Это в действительности позволяет вам требовать целый или плавающий тип в соответствии с его размером. Вы можете также указать вид 'byte', чтобы указать вид, соответствующий - 113 - однобайтовому целому, 'word' для вида однословного целого и 'pointer' для вида, используемого для представления указателей. nocommon Этот атрибут указывает GNU CC помещать переменную "общей", а выделять место для нее прямо. packed Атрибут packed указывает, что переменная или поле структуры должно иметь минимальное возможное выравнивание - один байт для переменной и один бит для поля, если вы не указали большее значение с помощью атрибута aligned. Ниже показана структура, в которой поле x запаковано так, что оно непосредственно следует за a: struct foo { char a; int x[2] __attribute__ ((packed)); }; section ("имя-секции") Обычно компилятор помещает объекты, которые он генерирует в секции типа data и bss. Однако, иногда вам нужны дополнительные секции, или вам нужно, чтобы определенные переменные оказались в специальных секциях, например, чтобы отобразить специальное оборудование. Атрибут section указывает, что переменная (или функция) живет в определенной секции. Например, эта маленькая программа использует несколько особых имен секций: struct duart a __attribute__ ((section ("DUART_A"))) = { 0 }; struct duart b __attribute__ ((section ("DUART_B"))) = { 0 }; char stack[10000] __attribute__ ((section ("STACK"))) = { 0 }; int init_data_copy __attribute__ ((section ("INITDATACOPY"))) = 0; main() { - 114 - /* Инициализируем указатель стека */ init_sp (stack + sizeof (stack)); /* Инициализируем инициализированные данные */ memcpy (&init_data_copy, &data, &edata - &data); /* Включаем последовательные порты */ init_duart (&a); init_duart (&b); } Используйте атрибут section с инициализированным определением глобальной переменной, как показано в примере. GNU CC выдает предупреждение и игнорирует атрибут section в неинициализированном объявлении переменной. transparent_union Этот атрибут, примененный к переменной-аргументу функции, который является объединением, означает передавать аргумент, таким же образом, каким передавался бы первый член объединения. Вы можете также использовать этот атрибут с typedef для типа данных объединения, затем он применяется ко всем аргументам функций с этим типом. unused Этот атрибут, примененный к переменной, означает, что переменная, возможно, может быть неиспользуемой. GNU CC не будет порождать предупреждение для этой переменной. weak Атрибут weak описан в Разделе [Атрибуты Функций]. Для указания многочисленных атрибутов разделяйте их запятыми внутри двойных скобок. Например: '__attribute__ ((aligned (16), packed))'. 4.29. Указание Атрибутов Типов Ключевое слово __attribute__ позволяет вам указывать специальные - 115 - атрибуты struct и union типов при их определении. За этим ключевым словом следует спецификация атрибута в двойных скобках. Три атрибута поддерживаются в данный момент для типов: aligned, packed, transparent_union. Другие атрибуты допустимы для функций (см. Раздел [Атрибуты Функций]) и для переменных (см. Раздел [Атрибуты Переменных]). Вы можете указывать атрибуты с '__', окружающими каждое ключевое слово. Это позволяет вам использовать их в заголовочных файлах, не заботясь о том, что могут быть макросы с тем же именем. Например, вы можете использовать __aligned__ вместо aligned. Вы можете указывать атрибуты aligned и transparent_union либо в typedef объявлении, либо сразу после закрывающей скобки полного определения enum, struct или union типа, а атрибут packed - только после закрывающей скобки определения. aligned (выравнивание) Этот атрибут определяет минимальное выравнивание (в байтах) для переменных указанного типа. Например, объявление struct S { short f[3]; } __attribute__ ((aligned (8)); typedef int more_aligned_int __attribute__ ((aligned (8)); заставляет компилятор гарантировать, что каждая переменная, чей тип - struct S или more_aligned_int будет размещаться и выравниваться на по меньшей мере 8-байтовой границе. Заметим, что выравнивание любого данного struct или union типа, требуемое стандартом ANSI C будет по меньшей мере максимальным выравниванием из выравниваний всех членов рассматриваемого struct или union типа. Как в предыдущем примере, вы можете явно указать выравнивание (в байтах), которое вы хотите, чтобы использовал компилятор для данного типа. В качестве альтернативы, вы можете оставить размер выравнивания и только попросить компилятор выравнивать тип по максимальному полезному выравниванию для целевой машины, для которой вы - 116 - компилируете. Например, вы могли бы написать: struct S { short f[3]; } __attribute__ ((aligned)); Атрибут aligned может только увеличить выравнивание, но вы можете уменьшить его с помощью указания packed. См. ниже. Заметим, что эффективность атрибутов aligned может быть ограничена ограничениями вашего линкера. Во многих системах, линкер может только обрабатывать выравнивание переменных, не превышающее определенного предела. (Для некоторых линкеров максимальное поддерживаемое выравнивание может быть очень и очень малым.) См. документацию по вашему линкеру для дальнейшей информации. packed Этот атрибут, примененный к определению enum, struct или union типа, указывает, что для представления этого типа должно быть использовано минимальное количество памяти. Указание этого атрибута для enum, struct или union типа эквивалентно указанию атрибута packed для каждого члена структуры или объединения. Указание флага '-fshort-enums' в командной строке эквивалентно указанию атрибута packed для всех описаний enum. Вы можете указывать этот атрибут только после закрывающей скобки описания enum, но не в typedef объявлении. transparent_union Этот атрибут, присоединенный к описанию типа union, показывает, что любая переменная этого типа при передаче функции должна передаваться также, как передавался бы первый член объединения. Например: union foo { char a; int x[2]; } __attribute__ ((transparent_union)); - 117 - Для указания многочисленных атрибутов разделяйте их запятыми внутри двойных скобок. Например: '__attribute__ ((aligned (16), packed))'.
CC-BY-CA Анатольев А.Г., 31.01.2012