Расширения языка С
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.
Несколько слов о стеке. На стеке сохраняется состояние процессора при прерываниях, распределяется память для автоматических (локальных) переменных, в нем сохраняется адрес возврата и передаются параметры процедур. Адресация стека (в 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),
па — число аргументов,
а — указатель на первый элемент массива аргументов.