Страница 34 из 56
тов, как, например, функция printf. По этой причине вы не можете
использовать эллипсис (...) (т.е. опускать подразумеваемый
параметр) в определении функции типа pascal. (См. "Прототипы
функций" для понимания использования элипсиса при определении
функции с различным числом аргументов.)
Модификатор функции cdecl
-------------------------
Модификатор cdecl также является специфичным для Турбо Си.
Как и модификатор pascal, он используется с функциями или указа-
телями на функции. Его действие отменяет директиву компилятора -p
и разрешает вызывать функции как обычные функции Си. Например,
если вы при компиляции вышеприведенной программы установили опцию
-p, но захотели использовать printf, то должны поступить следую-
щим образом:
extern cdecl printf();
putnums(int i, int j, int k);
- 277,278 -
cdecl main()
{
putnums(1,4,9);
}
putnums(int i, int j, int k)
{
printf("And the answers are: %d, %d и %d\n",i,j,k);
}
Если программа компилируется с опцией -p, то все функции,
используемые из библиотеки времени выполнения, необходимо объяв-
лять как cdecl. Если вы посмотрите файлы заголовков (такие как
STDIO.H), то увидете, что каждая функция явно описана как cdecl,
т.е. заранее подготовлена к этому. Заметьте, что главная програм-
ма main должна быть также объявлена как cdecl, поскольку действие
стартового кода программы, написанного на Си, всегда начинается c
вызова модуля main.
Модификатор функции interrupt
-----------------------------
Модификатор interrupt также является специфическим для Турбо
Си. Функции interrupt специально введены для использования с век-
торами прерываний процессоров типа 8086/8088. Турбо Си будет ком-
пилировать функцию типа interrupt в дополнительную функцию, вход
и выход которой сохраняется в регистрах AX, BX, CX, DX, SI, DI,
ES и DS. Другие регистры: BP, SP, SS, CS и IP сохраняются как
часть последовательности Си вызова или как часть самой обработки
прерывания. Рассмотрим пример типичного определения функции типа
interrupt.
void interrupt myhandler()
{
. . .
}
Вы можете объявлять функции прерываний как функции типа
void. Функции прерываний могут объявляться для любой модели памя-
ти. Для всех моделей, исключая huge, в регистр DS заносится прог-
раммный сегмент данных. Для модели huge в DS заносится модульный
- 279,280 -
сегмент данных.
Модификаторы функций near, far и huge
-------------------------------------
Модификаторы near, far, и huge специфичны для Турбо Си. Они
могут комбинироваться с модификаторами cdecl или pascal, но не с
interrupt. Не-interrupt функции могут быть объявлены как near,
far, или huge. Они по умолчанию устанавливаются для данной модели
памяти. Near-функции используют near-вызовы, а far- или huge-фун-
кции используют far-вызовы инструкций. В моделях памяти tiny,
small, и compact специально неоговоренные функции по умолчанию
имеют тип near. В моделях medium и large неоговоренные функции по
умолчанию имеют тип far. В модели памяти huge по умолчанию ис-
пользуется тип huge. Функции типа huge и far одинаковы за тем ис-
ключением что в регистр DS заноситься адрес сегмента данных ис-
ходного модуля когда вызывается huge-функция и он сбрасывается
для far-функции. Функции типа huge обычно используются когда неб-
ходимо организовать интерфейс с кодом на языке ассемблера который
не может использовать некоторую память занятую из Турбо Си.
Прототипы функций (K&R 10.1.2)
------------------------------
При объявлении функций в K&R допускается только указание ее
имени, типа и скобок без параметров. Параметры (если они есть)
объявляются только во время явного определения самой функции.
ANSI стандарт и Турбо Си разрешают использовать прототипы
функций для объявления функции. Эти объявления включают информа-
цию о параметрах функции. Компилятор использует данную информацию
для проверки вызовов функций на соответствие данных, а также для
преобразования аргументов к требуемому типу. Рассмотрим следующий
фрагмент программы:
long lmax(long v1, long v2);
main()
{
int limit = 32;
char ch = 'A';
- 281,282 -
long mval;
mval = lmax(limit,ch);
}
Используя прототип функции для lmax, эта программа будет
преобразовывать параметры limit и ch к типу long, используя стан-
дартные правила преобразования, прежде чем они будут помещены в
стек для обращения к lmax. При отсутствии прототипа функции пара-
метры limit и ch были бы помещены в стек, соответственно, как це-
лое значение и символ; в этом случае в lmax передавались бы пара-
метры, не совпадающие по размеру и содержанию с ожидаемыми. Это
ведет к возникновению проблем. В то время как Си в K&R не выпол-
няет никакого контроля типа параметров или их числа, использова-
ние прототипов функций очень помогает выявлять "жучки" и другие
ошибки программистов.
Прототипы функций также помогают при документировании прог-
рамм. Например, функция strcpy имеет два параметра: исходную
строку и выходную строку. Вопрос - как их распознать? Прототип
функции
char *strcpy(char *dest, char *source);
делает это ясным. Если в файле заголовка имеются прототипы функ-
ций, то вы можете распечатать этот файл и получить большую инфор-
мацию необходимую для написания программы, вызывающей эти функ-
ции.
Описание функции с включенным в скобки единственным словом
void означает, что функция совсем не имеет аргументов
f(void)
В противном случае, в скобках указывается список параметров,
разделенных запятыми. Так, объявление может быть сделано в виде:
func(int *, long);
или с включением идентификаторов, как ниже:
func(int *count, long total);
- 283,284 -
В обоих случаях, указанных выше, функция func принимает два
параметра: указатель на тип int, названный count, и целую пере-
менную типа long, названную total. Включение идентификатора в
объявление имеет смысл лишь для вывода его в диагностическом со-
общении, в случае возникновения несоответствия типа параметров.
Прототип обычно определяет функцию как функцию, принимающую
фиксированное число параметров. Для Си функций, принимающих раз-
личное число параметров (таких, как printf), пропотип функции мо-
жет заканчиваться элипсисом (...), например
f(int *count, long total,...)
У прототипов такого вида фиксированные параметры проверяются
во время компиляции, а опущенные передаются, как при отсутствии
прототипа.
Рассмотрим несколько примеров объявления функций и прототи-
пов.
int f(); /* Функция возвращает величину типа int без
информации о параметрах. Это классический
стиль K&R */
int f(void); /* Функция возвращает значение типа int, */
/* параметры не передаются */
int p(int,long); /* Функция возвращает целое, а получает */
/* два параметра; */
/* первый имеет тип int, второй - long */