C++ Программирование в среде С++ Builder 5

Расширения языка С


C++Builder поддерживает использование ряда ключевых слов, отсутствующих в стандартных ANSI C/C++. В таблице 4.4 перечислены все такие ключевые слова, которые могут применяться в программах на С. Многие из них могут записываться с одним или двумя начальными символами подчеркивания либо без них. Это сделано для того, чтобы можно было переопределить в препроцессоре какое-либо ключевое слово (например, форму без подчеркивания), сохранив возможность использования исходного слова (в форме с подчеркиванием). Рекомендую вам всегда пользоваться формой с двумя подчеркиваниями.

Таблица 4.4. Расширения набора ключевых слов языка С

Ключевые слова

Описание

asm

_asm

__asm

Позволяет вводить код ассемблера непосредственно в текст программы на C/C++. Синтаксис:



__asm операция операнды ;_ или перевод_ строки

Можно сгруппировать сразу несколько инструкций ассемблера в одном блоке asm:

__asm {

группа_ инструкций

}

cdecl

_cdecl

__cdecl

Специфицирует функцию как вызываемую в соответствии с соглашениями языка С. Перекрывает установки по умолчанию, сделанные в IDE или препроцессорных директивах.
_Except Служит для управления исключениями в программах на С.
_Export

__export

Служит для экспорта из DLL классов, функций или данных. (См. главу 2, где приведен пример построения DLL.)
_fastcall

__fastcall

Специфицирует функцию как вызываемую в соответствии с соглашением fascall (передача параметров в регистрах).
_Finally Служит для управления исключениями в программах на С.
_Import

__import

Импортирует классы, функции или данные, находящиеся в DLL.
_Inline Применяется для объявления в программах на С расширяемых функций (inline). Соответствует ключевому слову inline, которое имеется только в C++.
_Pascal

__pascal

___pascal

Специфицирует функцию как вызываемую в соответствии с соглашениями языка Pascal.
_stdcall

__stdcall

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



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



Соглашение о вызове определяет способ передачи параметров от вызывающей функции в вызываемую. То или иное соглашение может быть установлено по умолчанию в диалоге 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) ;

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

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

    ONONCONTINUABLE),

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

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


    Содержание раздела