Страница 43 из 56
Объявление указателей как NEAR, FAR или HUGE
-----------------------------------------------------------------
Вы уже видели, зачем вам может понадобиться объявлять функ-
ции для различных моделей памяти, применяемых в программе. А за-
чем может понадобиться делать то же самое с указателями? По при-
чинам, что уже описанны выше: во избежание необоснованного
расширения (объявив near, когда по умолчанию должно быть far) или
чтобы обратиться к чему-либо, находящемуся вне допустимого сег-
мента (объявив far или huge, когда по умолчанию должно быть
near).
Встречаются, конечно, потенциальные ловушки в присвоении
функциям и указателям типов, отличных от принятых по умолчанию.
Например, предположим, вы имеете следующую малую модель програм-
мы:
void myputs(s)
char *s;
{
int i;
for (i = 0; s[i] != 0; putc(s[i]);
}
main()
{
char near *mystr;
mystr = "Hello, world\n";
myputs(mystr);
{
Эта программа работает прекрасно, а объявление mystr как
near излишне, т.к. все указатели, как программы, так и данных,
будут near.
Но что будет, если вы перекомпилируете эту программу, ис-
пользуя компактную (или большую, или огромную) модель памяти?
Указатель mystr в main будет все еще near (это по-прежнему 16-би-
товый указатель). Однако указатель s в myputs теперь far, т.к.
это устанавливается по умолчанию. Это означает, что myputs будет
выбирать 2 слова из стека, создавая far-указатель, и полученный
адрес будет, несомненно, другим, чем у mystr.
- 353,354 -
Как вам решить эту проблему? Решается она следующим образом:
myputs объявляется в стиле современного Си, т.е.:
void myputs(char *s);
{
/*body of myputs*/
}
Теперь, когда Турбо Си компилирует вашу программу, ему из-
вестно, что myputs предполагает указатель на char, и, т.к. вы
компилируете в рамках большой модели, указатель должен быть far.
Поэтому Турбо Си будет помещать регистр сегмента данных (DS) в
стек совместно с 16-битной величиной mystr, формируя far-указа-
тель.
Как быть в обратном случае: параметры для myputs объявлены
как far и компилируются для малой модели памяти? Без использова-
ния прототипа функции вы столкнетесь с проблемами, т.к. main бу-
дет помещать в стек как смещение, так и адрес сегмента, но myputs
предполагает только смещение. При определении прототипа функции
main будет помещать в стек только смещение.
Вывод: если вы собираетесь использовать явное объявление
указателей как near или far, то надежнее всего использовать про-
тотипы функций для любых функций, где вы планируете их использо-
вать.
- 355,356 -
Способ указания на данный сегмент: Offset адрес (смещение)
-----------------------------------------------------------------
Каким образом вы можете указатель типа far разместить в дан-
ной ячейке памяти (специальном сегменте - offset адресе)? Вы мо-
жете воспользоваться встроенной библиотечной подпрограммой MK_FP,
которая выбирает сегмент и смещение и возвращает far- указатель.
Например:
MK_FP(segment_value, offset_value)
Данный far-указатель (fp) может быть и сегментом FP_SEG(fp),
и смещением FP_OFF(fp). Для более детального ознакомления с этими
тремя библиотечными подпрограммами Турбо Си следует обратиться к
"Справочному Руководству по Турбо Си".
Построение простых операторов объявления
-----------------------------------------------------------------
Операторы объявления в Си вы используете для объявления ти-
пов функций, переменных, указателей и данных. Си позволяет вам
строить комплексные операторы объявления. В этом разделе даются
некоторые примеры операторов объявления, которые могут помочь вам
в их построении (и прочтении); будет также показано, как избежать
некоторые ловушки.
Традиционно программирование на Си дает возможность сразу
строить полный оператор объявления, вставляя при необходимости
определения. К сожалению, это делает программы сложными для чте-
ния (и написания).
Например, рассматриваемые в таблице 12.3 операторы объявле-
ния предполагают, что вы компилируете в рамках малой модели памя-
ти (малая программа, малые данные).
ННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННН
int f1(); Функция, возвращающая целое.
- 357,358 -
int *p1; Указатель на целое.
int *f2(); Функция, возвращающая указа-
тель на целое.
int far *p2; Far- указатель на целое.
int far *f3(); Near- функция, возвращающая
far- указатель на целое.
int * far f4(); Far-функция, возвращающая
near-указатель на целое.
int (*fp1) (int); Указатель на функцию, возвра-
щающую целое и принимающую
целое.
int (*fp2) (int *ip); Указатель на функцию, возвра-
щающую целое и принимающую
указатель на целое.
int (far *fp3)(int far *ip) Far-указатель на функцию,воз-
вращающую целое и принимающую
far-указатель на целое.
int (far *list[5]) (int far *ip);
Массив из 5 far-указателей на
функции, возвращающие целое и
принимающие far- указатели на
целое.
int (far *gopher(int (far *fp[5])(int far *ip)))(int far *ip);
Near-функция, принимающая мас-
сив из 5 far-указателей на фу-
нкции, возвращающие целое и
принимающие far- указатели на
целое, и возвращающая один из
этих указателей
НННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННННН
Таблица 12.3. Объявление указателей без typedef
- 359,360 -
Здесь представлены все допустимые операторы объявления в по-
рядке возрастания трудности их восприятия. Однако, благоразумно
используя typedef, вы можете усилить четкость восприятия этих
операторов.
Ниже представлены те же операторы, записанные с использова-
нием typedef.