46019 (665326), страница 29
Текст из файла (страница 29)
| | \!-------------------------\! | Свободная
| | \! \!-|--область
|SP(TOS)--|->\!-------------------------\! | памяти
| | \!^ \! |
| \ \! стек| \!/
| Начало SP----->\!--------------------------
| \! дальняя| \! До конца
| \! кучаv \! памяти
| \!-------------------------\! Свободная
Старший | \! \!----область
адрес v --------------------------- памяти
Рис.4.4 Сегментация для модели памяти small
Сегментные регистры: Размер сегмента:
Младший ^ ---------------------------
адрес | \! _TEXT класс 'CODE'\! до 64К
| \! sfileкод \!каждый sfile
| DS,SS--------> \!---/---------------------\!
| / \! / _DATA класс 'DATA' \!\
| Несколько __|__\!_/инициализирован. данные\! |
| --------- | \!-------------------------\! |
| \!sfile A\! | \! _BSS класс 'BSS' \! |
CS->\!sfile B\! | \!неинициализирован. данные\! \ до 64К | \! \! | \!-------------------------\! /
| \!sfile Z\! | \! куча| \! |
| --------- | \!v \! |
|DGROUP/ \!-------------------------\! | Свободная
|\ \! \!-|--область
|SP(TOS)--|->\!-------------------------\! | памяти
| | \!^ \! |
| \ \! стек| \!/
| Начало SP----->\!--------------------------
| \! дальняя| \! До конца
| \! кучаv \! памяти
| \!-------------------------\! Свободная
Старший | \! \!----область
адрес v --------------------------- памяти
Рис.4.5 Сегментация для модели памяти medium
CS указывает одновременно только на один sfile.
Сегментные регистры: Размер сегмента:
Младший ^ CS-----------> --------------------------- адрес | \! _TEXT класс 'CODE' \!
| \!код \! до 64К
| DS ----------> \!-------------------------\!
| / \! _DATA класс 'DATA' \!\
| | \!инициализированные данные\! |
|DGROUP/ \!-------------------------\! \ до 64К
|\ \! _BSS класс 'BSS' \! /
| | \!неинициализирован. данные\! |
| \ \! \!/
| SS ---------> \!-------------------------\! Свободная
| \! \!----область
|SP(TOS)---->\!-------------------------\! памяти
| \!^ \!
| \! стек| \! до 64К
| Начало SP----->\!--------------------------
| \! куча| \! До конца
| \!v \! памяти
| \!-------------------------\! Свободная
Старший | \! \!----область
адрес v --------------------------- памяти
Рис.4.6 Сегментация для модели памяти compact
Несколько
\!sfile A\!
CS->\!sfile B\!
\! \!
\!sfile Z\!<---
--------- \
\
\
\
\
Сегментные регистры: \ Размер сегмента:
Младший ^ ----\----------------------
адрес | \! \ _TEXT класс 'CODE'\! до 64К
| \! sfileкод \!каждый sfile
| DS ----------> \!-------------------------\!
| / \! _DATA класс 'DATA' \!\
| | \! инициализирован. данные\! |
|DGROUP/ \!-------------------------\! \ до 64К
|\ \! _BSS класс 'BSS' \! /
| | \!неинициализирован. данные\! |
| \ \! \!/
| SS ---------> \!-------------------------\! Свободная
| \! \!----область
|SP(TOS)---->\!-------------------------\! памяти
| \!^ \!
| \! стек| \! до 64К
| Начало SP----->\!--------------------------
| \! куча| \! До конца
| \!v \! памяти
| \!-------------------------\! Свободная
Старший | \! \!----область
адрес v --------------------------- памяти
Рис.4.7 Сегментация для модели памяти large
Несколько
\!sfile A\!
CS->\!sfile B\!
\! \!
\!sfile Z\!<---
--------- \
\
\
\
\
Сегментные регистры: \ Размер сегмента:
Младший ^ ----\----------------------
адрес | \! \ _TEXT класс 'CODE'\! до 64К
| \! sfileкод \!каждый sfile
| \!-------------------------\!
| Несколько \! \!
| --------- \! \!
| \!sfile A\! \! \!
| DS->\!sfile B\!<-\!sfile _DATA класс 'DATA' \! до 64К
| \! \! \!неинициализирован. данные\! каждый sfile
| \!sfile Z\! \! \!
| SS ---------> \!-------------------------\! Свободная
| \! \!----область
|SP(TOS)---->\!-------------------------\! памяти
| \!^ \!
| \! стек| \! до 64К
| Начало SP----->\!--------------------------
| \! куча| \! До конца
| \!v \! памяти
| \!-------------------------\! Свободная
Старший | \! \!----область
адрес v --------------------------- памяти
Рис.4.8 Сегментация для модели памяти huge
Таблица 4.1. суммирует различные модели, а также результаты их сравнения друг с другом. Эти модели часто группируются в соответствии с тем, насколько малы (64К) или велики (1М) размеры их модулей кода и данных; Эти группы соответствуют рядами колонкам табл. 4.1.
-----------------------------------------------------------
Размер \! Размер кода
данных \! ------------------------------------------------- \! 64 K \!1 Mb
----------------------------------------------------------- \! Tiny (данные, коды \!
64K\! перекрываются \!
\! максимальный размер 64К) \!
\! \!
\! Small (без перекрытия \! Medium (данные - small
\! максимальный размер 128К) \! коды - large)
\! \!
1Mb\! Compact ( данные - large \! Large ( данные и коды \! коды - small) \! large)
\! \!
\! \! Huge (также как и large,
\! \! но cтатические
\! \! данные >64 K )
Важно!
Когда Выкомпилируете модуль (исходный файл с некоторым количеством процедур в нем), то результирующий код для этого модуля не может превышать 64К, т.к. он должен вмещаться в один кодовый сегмент. Это остается правилом, даже если Вы используете один из больших кодовых модулей (medium, large, huge). Если ваши модули очень велики ( > 64К ), Вы должны разбить их на несколько маленьких исходных файлов, компилировать их раздельно, а потом собирать в один файл. Аналогичным образом, несмотря на то, что huge модель позволяет набору статических данных превышать размер 64 К, все равно он должен быть < 64K в каждом модуле.
Программирование с использованием различных моделей памяти: адресные модификаторы
Turbo C++ вводит 8 новых ключевых слов, не имеющихся в стандартном ANSI C ( near, far, huge, _cs, _ds, _es, _ss, _seg), которые могут использоваться в качестве модификаторов адресных указателей (а иногда и функций) с некоторыми ограничениями.
В Turbo C ++ вы можете модифицировать объявления функций и адресных указателей с помощью ключевых слов near, far, huge. Мы уже объяснили смыслуказателей near, far, huge ранее в этой главе. near-функции вызываются near-вызовами с последующим near-выходом из них. Аналогичным образом far-функции вызываются far-вызовами с последующим far-выходом из них. huge-функции аналогичны far-функциям,за исключением того,что huge-функции устанавливают регистр DS в новое значение.
Кроме того имеются четыре специальных near-указателей данных: _cs, _ds, _es, _ss. Это шестнадцатибитовые указатели, которые специально ассоциируются с соответствующими сегментными регистрами, например, если бы вы должны были объявитьуказатель равным:
char _ss *p;
то рсодержал бы в этом случае шестнадцатибитовое смещение в сегменте стека.
Существует некоторое ограничение на использование сегментных указателей:
- Выне можете делать инкремент и декремент с указателями сегментов. Когда выприбавляете или вычитаете целое из сегментного указателя, он автоматически преобразуетсяв far-указатель, а арифметическая операция выполняетсятак, как если бы целое было прибавлено или вычтено из far-указателя.
- Когда сегментные указатели используются в косвенном выражении они также преобразуются в far-указатели.
- Также как и расширениек двоичному + оператору, если сегментный указатель прибавляется к near-указателю, результатом будет far-указатель, который формируется путем взятия сегмента из сегментного указателя исмещения из near-указателя. Такая операция разрешается, только если оба указателя указывают на одинаковый тип, илиесли один из указателей указывает на тип void.
- Указатели сегментов можно сравнивать. Сравнение их выполняется таким образом, как если бы их значения имели целочисленный тип unsigned.
Функции и указатели в данной программе по умолчанию бывают ближними или дальними, в зависимости от выбранной модели памяти. Если функция или указатель являются ближними, то они автоматически связываются с регистром CS или DS.
В следующей таблице показано, как это происходит. Отметим, что размер указателя соответствует предельному размеру памяти, равному 64К (ближний, в пределах сегмента) или 1 Мб (дальний, содержит собственный адрес сегмента).
Типы указателей Таблица 4.2
Модель памяти Указатели функции Указатели данных
Tiny near, _csnear, _ds
Small near, _csnear, _ds
Medium farnear, _ds
Compact near, _csfar
Large farfar
Huge farfar
Указатели данных могут быть также объявлены с модификатором _seg. Это 16-битовые указатели сегмента.
Объявление ближних или дальних функций
В некоторых случаях вам может захотеться(или понадобиться) переопределить умолчание типа функции для модели памяти, показанное в таблице 4.1 (стр.198 оригинала).
Например, вы используете модель памяти large, и в программе имеется рекурсивная функция:
double power(double x,int exp)
(*
if (exp <= 0)
return(1);
else
return(x * power(x, exp-1));
*)
Каждый раз, когда power вызывает сама себя, она должна выполнить дальний вызов, причем используется большая область стека и число тактовых циклов. Объявив power как near, можно ускорить выполнение ее благодаря тому, чтовызовыэтой функции будут ближними:
double near power(double x,int exp)
Это гарантирует, что power может быть вызвана только из того кодоваго сегмента, в котором она компилировалась, и что все обращения к ней будут ближними.
Это означает, что при использовании большой модели памяти (medium, large или huge) power можно вызывать только из того модуля, в котором она определена.Прочиемодулиимеют свои собственные кодовые сегменты и не могут вызывать функции near из других модулей. Более того, ближняя функциядо первого кней обращения должна быть либоопределена, либо объявлена, иначе компилятор не знает о необходимости генерировать ближний вызов.
И наоборот, объявление функции как дальней означает генерированиедальнего возврата. В малых моделях кодовой памяти дальняя функция должна быть объявлена или определена до первого к ней обращения, что обеспечит дальний вызов.
Вернемся к примеру функции power. Хорошо также объявить power как static, поскольку предусматривается вызывать ее только из текущего модуля. Если функция будет объявлена как static, тоимя ее не будет доступно ни одной функции вне данного модуля.
Объявление указателей near, far или huge
Только что были рассмотрены случаи, в которых может понадобиться объявить функциюс другой моделью памяти, нежели остальная часть программы. Зачем то же самое может понадобиться для указателей? По тем же причинам, что и дляфункций: либодля улучшения характеристик быстродействия (объявив near там, где по умолчанию было бы far), либо для ссылки за пределы сегмента по умолчанию (объявив far илиhuge там, где по умолчанию бывает near).
Разумеется, при объявлении функций или указателей с другим типом, нежели по умолчанию, потенциально появляется возможность ошибок. Предположим, имеется следующий пример программы с моделью small:
void myputs(s)
char *s;
(*
int i;
for (i = 0; s[i] != 0; i++) putc(s[i]);
*)
main()
(*
char near *mystr;
mystr = "Hello, world\n";
myputs(mystr);
*)
Эта программа работаетудовлетворительно, хотя объявление mystr как near избыточно, поскольку все указатели, как кода, так и данных, будут near по умолчанию.
Однако, что произойдет, если перекомпилировать эту программу с моделью памяти compact (либо large или huge)? Указатель mystr в main останется ближним (16-битовым). Однако, указатель s в myputs теперь будет far, поскольку умолчанием теперь будет являться far. Это означает, что попытка создания дальнего указателяприведет к снятию со стека двух слов, и полученный таким образом адрес, безусловно, не будет являться адресом mystr.
Как избежать этой проблемы? Решение состоит втом, чтобы определить myputs в современном стиле С:
void myputs(char *s)
(*
/* тело myputs */
*)
Теперь при компиляции вашей программы Turbo C++ знает, что myputs ожидает указатель на char; и поскольку компиляция выполняется с моделью large, то известно, что указатель должен быть far. Вследствие этого Turbo C++ поместит в стек регистр сегмента данных (DS) и 16-битовоезначение mystr, образуя тем самым дальний указатель.
Если вы собираетесь явнообъявлять указатели как farили near,не забывайте использовать прототипы тех функций, которые могут использовать эти указатели.
Как быть в обратном случае: когда аргументы myputs объявлены как far, а компиляция выполняется с моделью памяти small? И в этом случае без прототипа функции у вас возникнут проблемы, поскольку main будет помещать в стек и смещение, и адрес сегмента, тогда как myputs будет ожидать приема только одного смещения. При наличии определений функций в прототипах main будет помещать в стек только смещение.
Создание указателя данного адреса сегмент:смещение
Как создать дальний указательна конкретный адрес памяти (конкретный адрессегмент:смещение)? Для этого можно воспользоваться встроеннойбиблиотечной подпрограммой MK_FP, которая в качестве аргументапринимает сегмент и смещение, и возвращает дальний указатель. Например,
MK_FP(segment_value, offset_value)
Имея дальний указательfp, вы можете получить компонент сегмента полного адреса с помощью FP_SEG(fp) и компонент смещения с помощью FP_OFF(fp). Более полную информацию об этих трех библиотечных функциях Turbo C++ см. в Справочнике по библиотеке.
Использование библиотечных файлов
Turbo C++ предлягает для каждой из шести моделей памяти собственную версию библиотеки стандартных подпрограмм. Turbo C++ при этом проявляет достаточно "интеллекта", чтобы при компоновке брать нужные библиотеки и в нужной последовательности, в зависимости от выбранной вами модели памяти. Однако,при непосредственном использовании компоновщика Turbo C++ TLINK (как автономного компоновщика) вы должны явно указывать используемые библиотеки. Более подробно это описано в разделе TLINK Главы 5Б "Утилиты", Руководства пользователя.
Компоновка смешанных модулей
Что произойдет,если вы компилируете один модуль с использованием модели памяти small, второй - модели large, и затем хотите скомпоновать их? Что при этом произойдет?
Файлы скомпонуются удовлетворительно, но при этом вы встретитесь с проблемами, которые будут аналогичны с описанными выше в разделе "Объявление функций как near или far". Если функция модуля с моделью small вызывает функцию в модуле с моделью large, она будет использовать при этом ближний вызов, что даст абсолютно неверные результаты.Кроме того, у вас возникнут проблемы с указателями, описанные в разделе "Объявление указателей как near, far или huge", поскольку функция в модуле small ожидает, что принимаемые и передаваемые ейуказатели будут near, тогдакак функция в модуле large ожидает рабрту с указателями far.
И снова решение заключается в использовании прототипов функций. Предположим, что вы поместили myputs в отдельный модуль и скомпилировали его с моделью памяти large. Затем вы создаете файл заголовка myputs.h (либо с любым другим именем и расширением .h), который содержит следующий прототип функции:
void far myputs(char far *s);
Теперь, если поместить main в отдельный модуль (MYMAIN.
C) и выполнить следующие установки:
#include
#include "myputs.h"
main()
(*
char near *mystr;
mystr = "Hello, wirld\n";
myputs(mystr);
*)
то при компиляции данной программы Turbo C++ считает прототип функции из MYPUTS.H и увидит, что это дальняя функция, ожидающая дальний указатель. Вследствие этого даже при модели памяти small при компиляции будет сгенерирован правильный вызывающий код.
Что произойдет, если помимо этого вам требуется компоновка с библиотечными подпрограммами? Лучший подход здесь заключается в том, чтобы выбрать одну из библиотек с моделью large и объявить все как far. Для этого сделайте копии всех файлов заголовка, которые вы обычно включаете (таких, как stdio.h) и переименуйте эти копии (например, fstdio.h).
Затем отредактируйте копии прототипов функций таким образом, чтобы там было явно указано far, например:
int far cdecl printf(char far* format, ...);
Тем самым, не только вызовы подпрограмм будут дальними, но и передаваемые указатели также будут дальними.Модифицируйте вашу программу таким образом, чтобы она включала новый файл заголовка:
#include
main()
(*
char near *mystr;
mystr = "Hello, world\n";
printf(mystr);
*)
Скомпилируйте вашу программу при помощиTCC, затем скомпонуйте ее при помощью TLINK, указав библиотеки с моделью памяти large, напрмер CL.LIB. Смешиваниемодулей с разными моделями - вещь экстравагантная, но допустимая; будьте, однако, готовы к тому, чтолюбые неточности здесь приводят к ошибкам, которые очень трудно найти и исправиь при отладке.
Опции типа чисел с плавающей точкой
С работает с двумя числовыми типами:интегральным (int, short, long и т.д.) и с плавающей точкой (float double и long double). Процессор вашего компьютер легко справляется с обработкой чисел интегральных типов, однако числа с плавающей точкой отнимают больше времени и усилий.