Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
103
Добавлен:
27.03.2015
Размер:
465.41 Кб
Скачать

Соглашения о вызове

Соглашение о вызове определяет способ передачи параметров от вызывающей функции в вызываемую. То или иное соглашение может быть установлено по умолчанию в диалоге Project Options либо определяться модификатором в объявлении конкретной функции, например:

void _stdcall SomeDLLFunc(void);

Рассмотрим по порядку различные протоколы вызова, поддерживаемые в C+4-Builder.

  • Соглашение _cdecl является стандартным для программ на C/C++. Оно характеризуется тем, что аргументы при вызове помещаются на стек в порядке справа налево, и за очистку стека отвечает вызывающий. Кроме того, для функций _cdecl компилятор генерирует внутренние имена, начинающиеся с подчеркивания и сохраняющие регистр букв. Таким образом, внутренним именем функции SomeCFunc •будет _SomeCFunc.

  • Соглашение _pascal соответствует протоколу вызова функций в языке Pascal. Параметры помещаются на стек в порядке слева направо, а за очистку стека отвечает вызываемый. Внутреннее имя образуется переводом всех символов в верхний регистр; например, функция SomePascalFunc получит имя SOMEPASCALFUNC. Этот протокол вызова может быть более эффективен, чем _cdecl, особенно если функция вызывается из многих различных мест программы. Однако вызываемые таким образом функции не могут иметь переменного списка аргументов, как функции С.

  • Соглашение stdcall принято в 32-битных версиях Windows в качестве стандартного. Оно является своего рода гибридом двух предыдущих. Параметры помещаются в стек справа налево, однако за очистку стека отвечает вызываемый. Внутреннее имя совпадает с объявленным.

  • Соглашение fastcall широко применяется в визуальном программировании C++Builder, т. е. в библиотеке VCL. Первые три параметра, если это возможно, передаются в регистрах ЕАХ, ЕСХ и EDX. Параметры с плавающей точкой или структуры передаются через стек. Внутреннее имя образуется присоединением символа @; например, внутренним именем функции SomeFastFunc будет @SomeFastFunc.

Несколько слов о стеке. На стеке сохраняется состояние процессора при прерываниях, распределяется память для автоматических (локальных) переменных, в нем сохраняется адрес возврата и передаются параметры процедур. Адресация стека (в 32-битных системах) производится посредством специальных адресных регистров процессора — указателя стека ESP и базы стека ЕВР. Адрес, на который указывает регистр ESP, называют вершиной стека. Основные операции при работе со стеком — это PUSH (втолкнуть) и POP (вытолкнуть). Операция PUSH уменьшает значение указателя стека и записывает последний по полученному адресу. Операция POP считывает значение со стека в свой операнд и увеличивает указатель стека.

В 32-битном режиме адресации стек выравнивается по границе двойного слова, т. е. при операциях PUSH и POP значение ESP всегда изменяется на 4. Таким образом, стек при заполнении расширяется сверху вниз, и вершина стека является на самом деле нижней его точкой, т. е. имеет наименьший адрес.

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

bсс32.ехе -S myfile.c

Псевдопеременные

Псевдопеременные C++Builder служат представлением аппаратных регистров процессора и могут использоваться для непосредственной записи и считывания их содержимого. Регистровые псевдопеременные имеют такие имена:

_AL _AH _AX _ЕАХ

_BL _BH _ВХ _ЕВХ

_CL _CH _СХ __ЕСХ

_DL _DH _DX __EDX

_CS _DS _ES _SS

_SI _DI _ESI _EDI

_BP _SP _EBP _ESP

_FS _GS _FLAGS

Псевдопеременные могут применяться везде, где допускается использование целой переменной. Регистр флагов содержит информацию о состоянии процессора и результате последней инструкции.

Управление исключениями

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

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

В стандартном С нет средств для управления исключительными ситуациями, однако в C++Builder имеются три дополнительных ключевых слова (_try, _except и _finally), которые позволяют организовать в программе т. н. структурированное управление исключениями, которое отличается от стандартного механизма исключений, встроенного в C++.

Мы не будем сейчас подробно обсуждать механизмы обработки исключений, отложив это до тех времен, когда мы будем изучать специфические средства языка C++. Сейчас мы покажем только синтаксис _try/_except/_finally с краткими пояснениями.

Возможны две формы структурированной обработки исключений:

try

защищенный_блок_операторов

except(выражение)

блок_обработки исключения

либо

_try

защищенный_блок_опера торов

finally

блок_обработки_завершения

Защищенный блок содержит код, заключенный в фигурные скобки, который может возбудить исключение. При возбуждении исключения выполнение блока прерывается и управление передается обработчику, следующему за пробным блоком.. Блок обработки исключения исполняется только при возбужденном исключении и в зависимости от оценки выражения оператора _except. Это выражение должно принимать одно из трех значений:

EXCEPTION_EXECUTE_HANDLER

EXCEPTION_CONTINUE_SEARCH

EXCEPTION_CONTINUE_EXECUTION

Эти значения вызывают соответственно исполнение обработчика (блока __except), продолжение поиска обработчика (перевозбуждение исключения во внешнем блоке _try, если таковой имеется) или возобновление выполнения кода с той точки, где было возбуждено исключение.

Блок обработки завершения исполняется в любом случае, вне зависимости от того, возбуждено исключение или нет.

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

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

void RaiseException(DWORD ее, DWORD ef, DWORD na,

const DWORD *a) ;

где еe — код исключения,

ef — флаг исключения (EXCEPTION_CONTINUABLE либо EXCE.PTI-

ONONCONTINUABLE),

па — число аргументов,

а — указатель на первый элемент массива аргументов.

Управление компилятором

В этом разделе мы рассмотрим установки диалога Project Options, имеющие отношение к программам на С. В основном это будет касаться страниц Compiler и Advanced Compiler этого диалога. Он открывается выбором Project | Options в главном меню.

Страница Compiler

Эта страница диалога показана на рис. 4.1.

В нижней части страницы вы видите две кнопки: Full debug и Release. Первая из них выполняет все установки параметров, позволяющие в полной мере использовать возможности отладчика C++Builder; вторая запрещает генерацию какой-либо отладочной информации и оптимизирует код для получения максимальной скорости выполнения. При изучении языка вам лучше всего воспользоваться кнопкой Full debug и не задумываться больше об установках, влияющих на отладку и эффективность кода.

Рис. 4.1 Страница Compiler диалога Project Options

Коротко о разделах страницы Compiler.

  • Группа радиокнопок Code optimization позволяет полностью отключить оптимизацию, задать оптимизацию по скорости или выбрать отдельные опции оптимизации, включив радиокнопку Selected и нажав Optimizations. При этом откроется окно диалога со списком опций, в котором, кстати, показаны эквивалентные ключи командной строки, управляющие оптимизацией.

  • Группа Warnings управляет выдачей предупреждений. Включив соответствующую радиокнопку, можно запретить все, разрешить все предупреждения или управлять выдачей отдельных предупреждений. Диалог Compiler Warnings также показывает ключи командной строки. Я бы советовал вам разрешить выдачу всех предупреждений. Грамотно написанный код должен транслироваться не только без ошибок, но и без всяких замечаний со стороны компилятора.

  • Раздел Pre-compiled headers управляет прекомпиляцией заголовочных файлов.

Объем кода заголовочных файлов, включаемых в модуль исходный модуль, может достигать тысяч, если не десятков и сотен тысяч строк. К тому же часто эти заголовочные файлы включаются в каждый модуль проекта. Поскольку при разработке программы заголовочные файлы приходится изменять сравнительно редко (а стандартные заголовки вообще не меняются), имеет смысл компилировать все необходимые заголовки один раз и создать файл специального вида, который будет содержать всю необходимую “заголовочную” информацию в форме, обеспечивающей максимально быстрый доступ к ней. Компилятор C++Builder может генерировать такие файлы (с расширением .csm), •во много раз ускоряющие повторное построение проектов. Недостатком их можно считать разве что весьма большой размер — типичный файл прекомпилируемых заголовков может занимать от пяти до десяти мегабайт.

  • Кнопка None запрещает использование прекомпилируемых заголовков. Кнопка Use pre-compiled headers разрешает генерацию и использование файла компилированных символов (это другое название файлов .csm). Кнопка Cache pre-compiled headers заставляет компилятор кэшировать прекомпилируемые заголовки, т. е. хранить их информацию в памяти, а не загружать csm-файл заново, когда в этом возникает необходимость. Это полезно, когда вы транслируете сразу несколько файлов, но может и замедлять компиляцию, если память системы невелика. В поле редактирования File name задается имя файла компилированных символов. В поле Stop after можно ввести имя файла, после компиляции которого генерация прекомпилируемых заголовков прекращается. Это должен быть файл, включаемый в исходный модуль непосредственно, а не в другом заголовке (как, например, windows.h включает массу других заголовочных файлов). • Раздел Debugging управляет включением отладочной информации в объектные файлы, создаваемые компилятором (флажки Debug information и Line numbers). Кроме того, флажок Disable inline expansions позволяет запретить расширения inline-функций, т. е. непосредственную вставку кода функции на месте ее вызова. Это упрощает отладку.

Если вы хотите отлаживать программу, то должны убедиться, что флажок Create debug information на странице Linker также установлен.

  • Раздел Compiling управляет общими аспектами компиляции. При помеченном флажке Merge duplicate strings компилятор сопоставляет встречающиеся литеральные строки и, если две или более строк совпадают, генерирует только одну строку. Это делает программу несколько более компактной, но может приводить к ошибкам, если вы модифицируете одну из строк. При установке флажка Stack frames компилятор генерирует стандартные кадры стека функций, т. е. стандартный код входа и возврата. Этот флажок должен быть установлен, если вы хотите отлаживать 'функции модуля. Если флажок сброшен, то для функций, не имеющих параметров и локальных переменных, будет генерироваться нестандартный, сокращенный код. При установке Treat enum types as ints компилятор отводит под перечисления 4-байтовое слово. Если флажок сброшен, отводится минимальное целое (1 байт, если значения перечислимого типа лежат в диапазоне 0-255 или -128-127). Show general messages разрешает выдачу общих сообщений компилятора (не являющихся предупреждениями или сообщениями об ошибках). Флажок Extended error information разрешает выдачу расширенных сообщений об ошибках компилятора (вплоть до контекста синтаксического анализатора и т. п. — простому человеку ни к чему).

Страница Advanced Compiler

Эта страница (рис. 4.2) позволяет управлять деталями генерации объектного кода.

Рис. 4.2 Страница Advanced Compiler диалога Project Options

  • Группа радиокнопок Instruction set задает тип процессора целевой системы. Установки этой группы эквивалентны ключам командной строки -3, -4, -5 и -6.

  • Группа Data alignment управляет выравниванием данных в памяти. Выравнивание может отсутствовать (Byte), либо данные могут располагаться по адресам, кратным 2, 4 или 8 байтам. В структурах, если необходимо, вводятся байты заполнения. Установки группы эквивалентны ключам командной строки -an, где п — 1, 2, 4 или 8.

  • Группа Calling convention задает соглашение о вызове, применяемое по умолчанию. (Register обозначает соглашение _fastcall.) Эквивалентные ключи командной строки — -рс (или -р-), -р, -рг и -ps.

  • Группа Register variables управляет созданием регистровых переменных. None запрещает их использование. Automatic разрешает компилятору размещать переменные в регистрах там, где это целесообразно, и Register keyword разрешает размещать в регистрах только переменные, объявленные как register. Радиокнопки соответствуют ключам -г-, -г и -rd.

  • Раздел Output имеет два флажка: Autodepedency information и Generate

  • underscores. Первый из них определяет, будет ли включаться в объектный файл информация о зависимостях между исходными файлами (необходимо для работы команды Make). Второй флажок указывает, что имена функций _cdecl должны снабжаться начальным символом подчеркивания. Установленные флажки соответствуют ключам -Х- и -и.

  • Раздел Floating point управляет генерацией кода арифметики с плавающей точкой. None показывает, что ваш код не использует чисел с плавающей точкой. Fast исключает промежуточные преобразования типов при арифметических вычислениях, как явные, так и неявные;

при сброшенном флажке все преобразования выполняются в строгом соответствии с правилами ANSI С. Correct Pentium FDIV генерирует код, исключающий возможность ошибки из-за дефекта в ранних версиях процессора Pentium. Соответствующие ключи командной строки — -f-, -ff и -fp.

  • Группа радиокнопок Language compliance задает версию языка, с которой совместим компилируемый исходный код. Вы можете выбрать стандартный ANSI С, С с расширениями Borland, “классическую” версию Кернигана-Ричи и язык Unix V. При написании приложений Windows имеет смысл выбрать либо ANSI, либо Borland, если вы хотите использовать какие-либо ключевые слова из расширенного набора, описанного в этой главе. Флажкам соответствуют ключи -A (ANSI), -AT или -A- (Borland), -АК (К & R) и -AU (Unix V).

  • Раздел Source управляет некоторыми аспектами интерпретации исходного кода. Флажок Nested comments разрешает вложенные С-комментарии, т. е. конструкции вида /*.../*...*/...*/. MFC compatibility позволяет транслировать код библиотеки MFC, используемой компилятором Microsoft Visual С. Поле Identifier length задает максимальное число значимых символов в идентификаторах языка С (в C++ длина идентификаторов не ограничивается).

Страница Directories/Conditionals

На этой странице диалога Project Options (рис. 4.3) расположены несколько полей редактирования, позволяющих задавать стандартные каталоги по умолчанию — библиотек, заголовочных файлов и т. д. Нас на этой странице интересует сейчас только раздел Conditionals.

В поле Conditional defines можно определять символы C/C++, языка Object Pascal и компилятора ресурсов, которые будут, например, управлять директивами условной компиляции в исходных файлах. Для присвоения символам значений используется знак равенства. Можно ввести в это поле сразу несколько определений, отделяя их друг от друга точкой с запятой, например:

NDEBUG;ххх=1;yyy-YES

Для ввода определений можно также воспользоваться редактором строк, отрывающимся при нажатии кнопки с многоточием.

Рис. 4.3 Страница Directories/Conditronals

В командной строке символы определяются с помощью ключа -D:

bcc32 -DNDEBUG -Dxxx=l -Dyyy=YES ...

Мы немного рассказали о ключах командной строки компилятора не столько для того, чтобы вы умели запускать bcc32.ехе вручную, а чтобы дать вам некоторые г начальные сведения, которые помогут вам разбираться в bgr-файлах проектов C++Builder. Полное руководство по запуску компилятора из командной строки вы можете найти в оперативной справке в разделах command-line compiler и command-line options.

Соседние файлы в папке СИ