Logo Host-telecom.com — профессиональный хостинг в Европе! Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
ATLEX Выделенные серверы: в Европе / в России.

Виртуальные серверы: в Европе / в России.

Партнерская программа

Ваш сайт в 8 раз быстрее конкурентов. Хостинг от $2.95

VPS: SSD, KVM, бесплатные бэкапы и администрирование

Все необходимое для вашего сайта и лучшая техподдержка 24/7

хостинг сайтов ГиперХост — хостинг сайтов который Вы искали.

Виртуальный хостинг, Аренда VPS серверов, рация доменных имен, SSL сертификаты

💰 Самые низкие цены на домены

🔒 Отличный хостинг на SSD c бесплатными SSL

💻 Огромнейший выбор dedicated выделенных серверов

Справочник по работе с DOS

Ниже представлена информация, которая будет полезной при разработке 16-разрядных приложений для работы в DOS.

Управление памятью в DOS

Нехватка памяти при выполнении

Borland С++ при компиляции не генерирует на диске никаких промежуточных структур данных (записывая на диск только файлы .OBJ). Вместо этого для хранения промежуточных структур данных между проходами используется оперативная память. Поэтому при недостаточном объеме оперативной памяти вам может выводиться сообщение о нехватке памяти. Чтобы решить эту проблему, уменьшите размер функций или разбейте файл с крупными функциями на несколько частей.

Модели памяти

В Borland С++ используется 6 моделей памяти, каждая из которых служит для различных размеров программ и кода. Регистры процессора

Ниже представлены некоторые регистры процессоров 80х86. Эти процессора имеют и другие регистры, но непосредственно к ним обращаться нельзя, поэтому они здесь не показаны.

Регистры общего назначения

Аккумулятор (математические операции)
AX AH AL
 
Базовый регистр (индексирование)
BX BH BL
 
Счетчик (индексирование)
CX CH CL
 
Регистр данных
DX DH DL

Сегментные адресные регистры

CS Сегментный регистр кода
DS Сегментный регистр данных
SS Указатель сегмента стека
ES Дополнительный регистр сегмента

Регистры общего назначения

SP Указатель стека
BP Указатель базы
SI Индекс источника
DI Индекс приемника

Общие регистры чаще всего используются для работы с данными. Каждый из них выполняет некоторые специальные функции, которые доступны только ему, например, некоторые математические операции могут использовать только регистр AX, регистр BX может служить базовым регистром, CX применяется инструкцией LOOP и некоторыми строковыми инструкциями, а DX используется некоторыми математическими операциями неявно. Однако во многих операциях можно использовать все эти регистры и заменять один из них на другой.

Сегментные регистры содержат начальный адрес каждого из 4 сегментов. Как описывается ниже, 16-разрядное значение в сегментном регистре для получения 20-разрядного адреса сегмента сдвигается влево на 4 (умножается на 16).


                   16-разрядный сегментный           16-разрядное  



                          регистр                      смещение    



                            V                               



                     умножение на 16                        



                   /сдвиг влево на 4/                       



                            V                               



                    Значение сегмента,                      



                    умноженное на 16,                       



                    равно 20-разрядно-                      



                    му значению                             



                                      >   +   <             



                                          V



                          20-разрядное значение адреса памяти  



Процессоры 80х86 имеют также некоторые специальные регистры:

  • Регистры SI и DI могут выполнять многие функции общих регистров, но могут также использоваться в качестве индексных регистров. Они используются и в регистровых переменных Borland С++.
  • Регистр SP указывает на текущую вершину стека и представляет смещение в сегменте стека.
  • Регистр BP - это вспомогательный указатель стека, применяемый для индексирования в стеке с целью извлечения аргументов или локальных динамических переменных.

Функции Borland С++ используют регистр базы (BP) в качестве базового регистра для аргументов и переменных. Параметры имеют положительные смещения от BP, зависящие от модели памяти. При наличии кадра стека BP указывает на сохраненное предыдущее значение BP. Если параметр Standard Stack Frame выключен (Off), то функции без аргументов не используют и не сохраняют BP.

16-разрядный регистр флагов содержит все необходимую информацию о состоянии процессора 80х86 и результатах последних инструкций.


                  только 80386          286/386  все процессоры 80х86   



                                                                        



       31              23               15              7              0



                                                                        



                                    V R   N IOP O D I T S Z   A   P   C 



                                                                       



           Виртуальный режим 80х86                                     



                     Возобновление                                     



                  Вложенная задача                                     



       Уровень защиты ввода-вывода                                     



                      Переполнение                                     



                       Направление                                     



             Разрешение прерывания                                     



                        Прерывание                                     



                              Знак                                     



                      Признак нуля                                     



           Вспомогательный перенос                                     



                          Четность                                     



                           Перенос                                     



Например, если вы хотите знать, получен ли при вычитании нулевой результат, непосредственно после этой инструкции вам следует проверить флаг нуля (бит Z в регистре флагов). Если он установлен (то есть имеет ненулевое значение), это будет говорить о том, что результат нулевой. Другие флаги, такие, как флаги переноса и переполнения аналогичным образом сообщают о результатах арифметических и логических операций.

Прочие флаги управляют режимом операций процессора 80х86. Флаг направления управляет направлением, в котором строковые инструкции выполняют перемещение, а флаг прерывания управляет тем, будет ли разрешено внешним аппаратным средствам, таким, например, как клавиатура или модем, временно приостанавливать текущий код для выполнения функций, требующих немедленного обслуживания. Флаг перехвата используется только программным обеспечением, которое служит для отладки другого программного обеспечения (отладчики).

Регистр флагов не считывается и не модифицируется непосредственно. Вместо этого регистр флагов управляется в общем случае с помощью специальных инструкций (таких, как CLD, STI и CMC), а также с помощью арифметических и логических инструкций, модифицирующих отдельные флаги. И наоборот, содержимое отдельных разрядов регистра флагов влияет на выполнение инструкций (например, JZ, RCR и MOVSB). Регистр флагов не используется на самом деле, как ячейка памяти, вместо этого он служит для контроля за состоянием и управления процессором 8086.

Сегментация памяти

Память микропроцессора Intel 80x86 имеет сегментированную архитектуру. Непосредственно можно адресоваться к 64К памяти сегменту. Процессор 80x86 отслеживает 4 различных сегмента: сегмент кода, сегмент данных, сегмент стека и дополнительный сегмент. В сегменте кода находятся машинные инструкции, а в дополнительном сегменте - дополнительные данные. Процессор 80x86 имеет 4 16-разрядных сегмента (по одному на сегмент) - CS, DS, SS и ES, которые указывают на сегмент кода, данных, стека и дополнительный сегмент соответственно. Сегмент может находиться в любом месте памяти, но начинаться должен по адресу, кратному 10. Сегменты могут перекрываться. Например, все четыре сегмента могут начинаться с одного адреса.

Стандартная запись адреса имеет форму "сегмент:смещение", например, 2F84:0546. Начальный адрес сегмента всегда представляет собой 20-битовое число, но так как сегментный регистр содержит только 16 бит, нижние 4 бита полагаются равными 0. Это значит, что сегменты могут начинаться только с тех адресов, у которых последние 4 бита равны 0.

Указатели

Хотя указатель или функция могут иметь конкретный тип независимо от используемой модели, вы можете выбрать заданный по умолчанию тип указателя, используемый для кода и данных. Существует 4 типа указателей: near (16 бит), far (32 бита), huge (32 бита) и segment (16 бит).

В указателях near (ближние указатели) для вычисления адреса используется один сегментный регистр, например, 16-битовое значение указателя функции складывается со сдвинутым влево содержимым регистра кода CS. С такими указателями легко работать.

Указатели far (дальние указатели) содержат не только смещение в сегменте, но и адрес сегмента (другое 16-битовое значение). Такие указатели позволяют иметь несколько сегментов кода и программы, превышающие по размеру 64К. Здесь нужно учитывать, что в операциях == и != используются 32-битовые значения unsigned long, а не полный адрес памяти. В операциях сравнения <=, >=, < и > используется только смещение.

При прибавлении к указателю значения изменяется только смещение. Если смещение превышает FFFF (максимально возможное значение), то указатель возвращается к началу сегмента. При сравнении указателей лучше использовать ближние указатели или указатели huge.

Указатели huge также занимают 32 бита. Аналогично указателям far, они содержат и адрес сегмента и смещение. Однако, чтобы избежать проблем с указателями, такие указатели нормализуются. Нормализованный указатель - это 32-битовый указатель с максимально возможным значением в сегментном адресе. Так как сегмент может начинаться с каждых 16 байт, это означает, что данное смещение будет иметь значение от 0 до 15. Для нормализации указателя он конвертируется в 20-битовый адрес, а затем используются правые 4 бита смещения и левые 16 бит адреса сегмента. Например, 2F84:0532 преобразуется в абсолютный адрес 2FD72, который нормализуется в 2FD7:0002. Нормализация важна по следующими причинам:

  • Каждому сегментному адресу соответствует при этом только одна возможная адресная пара "сегмент:смещение". Это означает, что операции == и != возвращают корректный ответ.
  • В операциях сравнения <=, >=, < и > используется при этом полные 32-битовые значения. Нормализация обеспечивает корректность результатов.
  • Благодаря нормализации смещение в указателях huge автоматически циклически возвращаются каждые 16 байт, но настраивается также и сегмент. Например, при инкрементации 811B:000F результатом будет 811C:0000. Это обеспечивает, что, например, при наличии массива структур типа huge > 64К индексирование массива и выбор поля struct будет работать для структур любого размера.

Однако работа с указателями huge связана с дополнительными издержками. Из-за этого арифметические операции с указателями huge выполняются намного медленнее, чем с указателями far.

Модели памяти

В 16-разрядных программах Borland С++ вы можете использовать 6 моделей памяти: крохотную, малую, среднюю, компактную, большую и огромную.

  • Tiny (крохотная). Эта модель памяти используется в тех случаях, когда абсолютным критерием достоинства программы является размер ее загрузочного кода. Это минимальная из моделей памяти. Все четыре сегментных регистра (CS, DS, SS и ES) устанавливаются на один и тот же адрес, что дает общий размер кода, данных и стека, равный 64К. Используются исключительно ближние указатели. Программы со сверхмалой моделью памяти можно преобразовать к формату .COM (при компоновке с параметром /t).
  • Small (малая). Эта модель хорошо подходит для небольших прикладных программ. Сегменты кода и данных расположены отдельно друг от друга и не перекрываются, что позволяет иметь 64К кода программы и 64К данных и стека. Используются только указатели near.
  • Medium (средняя). Эта модель годится для больших программ, для которых не требуется держать в памяти большой объем данных. Для кода, но не для данных используются указатели far. В результате данные плюс стек ограничены размером 64К, а код может занимать до 1М.
  • Compact (компактная). Лучше всего использовать эту модель в тех случаях, когда размер кода невелик, но требуется адресация большого объема данных. Указатели far используются для данных, но не для кода. Следовательно, код здесь ограничен 64К, а предельный размер данных - 1 Мб.
  • Large (большая). Модели large и huge применяются только в очень больших программах. Дальние указатели используются как для кода, так и для данных, что дает предельный размер 1 Мб для обоих.
  • Huge (огромная). Дальние указатели используются как для кода, так и для данных. Borland C++ обычно ограничивает размер статических данных 64К; модель памяти huge отменяет это ограничение, позволяя статическим данным занимать более 64К.

Для выбора любой из этих моделей памяти вы должны либо воспользоваться соответствующим параметром меню интегрированной среды, либо ввести параметр при запуске компилятора, работающего в режиме командной строки.

Следующие иллюстрации показывают, как выполняется распределение памяти для всех шести моделей памяти Borland C++.

Сегментация для модели памяти tiny


                   Сегментные регистры:                 Размер сегмента:



        Младший ^  CS,DS,SS                                 



         адрес                         _TEXT класс 'CODE'      



                                              код              



                                                               



                                       _DATA класс 'DATA'      



                                   инициализированные данные   



                                                               



                                       _BSS класс 'BSS'        



                      DGROUP      не инициализирован.данные    до 64К



                                                               



                                   динамически                 



                                   распределя-                 



                                   емая область v              



                                                               Свободная



                                                                 область



                      SP(TOS)                                   памяти



                                              ^                



        Старший                         стек                   



         адрес  v  Начало SP                                



Сегментация для модели памяти small


                   Сегментные регистры:                  Размер сегмента:



        Младший ^  CS                                       



         адрес                         _TEXT класс 'CODE'    



                                              код              до 64К



                   DS,SS                                    



                                       _DATA класс 'DATA'      



                                   инициализированные данные   



                                                               



                                       _BSS класс 'BSS'        



                      DGROUP      не инициализирован.данные    до 64К



                                                               



                                   динамически                 



                                   распределя-                 



                                   емая об-                    



                                   ласть памя-                 



                                   ти         v                



                                                               Свободная



                                                                 область



                      SP(TOS)   -                               памяти



                                              ^                



                                        стек                   



                   Начало SP                                



                                   дальняя                   



                                   динамически                До конца



                                   распределя-                памяти



                                   емая область v            



                                                               Свободная



        Старший                                                  область



         адрес  v                                                памяти



Сегментация для модели памяти medium


              Сегментные регистры:                  Размер сегмента:



        Младший ^                                            



         адрес                            _TEXT класс 'CODE'   до 64К



                                    sfile     код            каждый



                   DS,SS                                     sfile



                                       _DATA класс 'DATA'      



                   Несколько        инициализирован. данные   



                   sfile                                       



                                       _BSS класс 'BSS'        



               CS  sfile A        не инициализирован.данные     до 64К



                    sfile B                                    



                                   динамически распределяе-    



                    sfile Z        мая область памяти          



                                                               



                                              v                



                      DGROUP                                  Свободная



                                                                 область



                      SP(TOS)                                   памяти



                                              ^                



                                        стек                   



                   Начало SP                                



                                   дальняя                   



                                   динамически                До конца



                                   распределя-                памяти



                                   емая область v            



                                                               Свободная



        Старший                                                  область



         адрес  v                                                памяти





CS указывает одновременно только на один sfile.

Сегментация для модели памяти compact


                   Сегментные регистры:                  Размер сегмента:



        Младший ^  CS                                       



         адрес                         _TEXT класс 'CODE'    



                                              код              до 64К



                   DS                                       



                                       _DATA класс 'DATA'      



                                   инициализированные данные   



                      DGROUP                                   до 64К



                                       _BSS класс 'BSS'        



                                   не инициализирован.данные   



                                                               



                   SS                                         Свободная



                                                                 область



                      SP(TOS)                                   памяти



                                              ^              



                                        стек                  до 64К



                   Начало SP                                



                                   динамически                До конца



                                   распределя-                памяти



                                   емая область v            



                                                               Свободная



        Старший                                                  область



         адрес  v                                                памяти



Сегментация для модели памяти large


                   Несколько



                     sfile



                            



                    sfile A 



               CS  sfile B 



                            



                    sfile Z           



                                       



                                       



              Сегментные регистры:                 Размер сегмента:



        Младший ^                                            



         адрес                            _TEXT класс 'CODE'   до 64К



                                    sfile     код            каждый



                   DS                                        sfile



                                       _DATA класс 'DATA'      



                                     инициализирован. данные   



                      DGROUP                                   до 64К



                                       _BSS класс 'BSS'        



                                   не инициализирован.данные   



                                                               



                   SS                                         Свободная



                                                                 область



                      SP(TOS)                                   памяти



                                              ^              



                                        стек                  до 64К



                   Начало SP                                



                                   динамически                До конца



                                   распределя-                памяти



                                   емая область v            



                                                               Свободная



        Старший                                                  область



         адрес  v                                                памяти





CS и DS указывают одновременно только на один sfile.

Сегментация для модели памяти Huge


                   Несколько



                     sfile



                            



                    sfile A 



                    sfile B 



                            



               CS  sfile Z          



                                      



                                      



              Сегментные регистры:                  Размер сегмента:



        Младший ^                                            



         адрес                       sfile_TEXT класс 'CODE'   до 64К



                                              код            каждый



                                                              sfile



                   Несколько                                 



                    sfile                                    



                                   sfile_DATA класс 'DATA'    до 64К



                    sfile A        инициализированные данные каждый



               DS  sfile B                                   sfile



                                                             



                    sfile Z                                  



                                                             



                      SS                                      Свободная



                                                                 область



                      SP(TOS)                                   памяти



                                              ^              



                                        стек                  До 64К



                   Начало SP                                



                                   динамически                До конца



                                   распределя-                памяти



                                   емая область v            



                                                               Свободная



        Старший                                                  область



         адрес  v                                                памяти





В следующей таблице сведены различные модели и их сравнение друг с другом. Модели часто группируются по модели кода или данных на малые (64К) и большие (16М); эти группы соответственно отражены в столбцах и строках таблицы.

Модели tiny, small и compact относятся к малым моделям кода, поскольку по умолчанию указатели кода являются ближними (near). Аналогичным образом, модели compact, large huge относятся к большим моделями данных, поскольку по умолчанию указатели на данные являются дальними (far).

Модели памяти


                                      Размер кода                       



        Размер данных                                                   



                                64К                     16Мб            



                                                                        



                       Tiny (данные и код                               



                       перекрываются; общий                             



                       размер = 64К)                                    



          64K                                                           



                       Small (без перекрытия;     Medium (данные small, 



                       общий размер = 128К)       код large)            



                                                                        



                       Compact (данные large,     Large (данные и код   



                       код small)                 large)                



          16Мб                                                          



                                                  Huge (то  же,  что  и 



                                                  large, но статические 



                                                  данные > 64K)         



При компиляции модуля (некоторый исходный файл с несколькими подпрограммами), результирующий код для этого модуля не может превышать 64К, поскольку весь файл должен компилироваться в один кодовый сегмент. Это верно и в том случае, когда вы используете одну из больших моделей памяти (medium, large или huge). Если ваш модуль слишком велик и не помещается в одном кодовом сегменте (64К), вы должны разбить его на несколько файлов исходного кода, скомпилировать каждый из них по отдельности и затем скомпоновать их в одну программу. Аналогичным образом, хотя модель huge и позволяет иметь размер статических данных больше чем 64К, в каждом отдельном модуле статические данные не должны превышать 64К.

Программирование со смешанными моделями и модификаторы адресации

Borland C ++ вводит восемь новых ключевых слов, отсутствующих в языке Си стандарта ANSI (near, far, huge, _cs, _ds, _es, _ss и _seg), которые с некоторыми ограничениями и предупреждениями могут использоваться в качестве модификаторов для указателей (и в некоторых случаях, для функций).

В Borland C++ при помощи ключевых слов near, far или huge вы можете модифицировать объявления функций и указателей. Указатели данных near, far и huge рассматривались в выше. Объекты far объявляются при помощи ключевого слова far. Функции near запускаются при помощи ближних вызовов (near), а выход из них происходит с использованием ближних команд возврата. Аналогичным образом, функции far вызываются дальними вызовами (far) и выполняют дальний (far) возврат. Функции huge похожи на функции far, за исключением того, что функции huge устанавливают регистр DS в новое значение, тогда как функции far не изменяют значения этого регистра.

Существует также четыре специальных ближних (near) указателя данных: __cs, __ds, __es и __ss. Имеются 16-битовые указатели, конкретно связанные с соответствующими сегментными регистрами. Например, если вы объявите указатель следующим образом:


            char _ss *p;



то p будет содержать 16-битовое смещение в сегмент стека.

Функции и указатели в данной программе по умолчанию бывают ближними или дальними, в зависимости от выбранной модели памяти. Если функция или указатель являются ближними, то они автоматически связываются с регистром CS или DS.

В следующей таблице показано, как это происходит. Отметим, что размер указателя соответствует предельному размеру памяти, равному 64К (ближний, в пределах сегмента) или 1 Мб (дальний, содержит собственный адрес сегмента).

Типы указателей

Модель памяти Указатели функции Указатели данных
Tiny near, _cs near, _ds
Small near, _cs near, _ds
Medium far near, _ds
Compact near, _cs far
Large far far
Huge far far

Указатели сегментов

В объявлениях типа указателя сегмента используется __seg. В результате получаются 16-битовые указатели сегментов. Синтаксис __seg следующий:


            тип_данных _seg *идентификатор





Например,

            int _seg *name





Любое обращение по ссылке через "идентификатор" предполагает смещение 0. В арифметических операциях с указателями выполняются следующие правила:

  1. Нельзя использовать с указателями сегментов операции ++, --, +- или -=.
  2. Нельзя вычитать один указатель сегмента из другого.
  3. При сложении сегментного указателя с ближним (near) указателем результатом будет дальний (far) указатель, который формируется из сегмента, задаваемого сегментным указателем, и смещения из ближнего указателя. Эта операция разрешена только в том случае, если два указателя указывают на один и тот же тип, либо если один из указателей указывает на тип void. Независимо от указываемого типа умножение смещения не происходит.
  4. Когда сегментный указатель используется в выражении обращения по ссылке, он также неявно преобразуется в дальний указатель.
  5. При выполнении операции сложения или вычитания целочисленного операнда и сегментного указателя результатом является дальний указатель, где сегмент берется из сегментного указателя, а смещение получается умножением размера объекта, на который указывает целочисленный операнд. Арифметическая операция выполняется таким образом, как если бы целое складывалось с указателем far или вычиталось из него.
  6. Сегментные указатели могут присваиваться, инициализироваться, передаваться в функции и из функций, сравниваться, и т.д. (Сегментные указатели сравниваются по правилам для unsigned int). Другими словами, за исключением перечисленных выше ограничений, они обрабатываются так же, как и любые другие указатели.

Объявление дальних объектов

Borland С++ позволяет объявлять дальние (far) объекты. Например:


            int far x = 5;



            int far z;



            extern int far y = 4;



            static long j;



Компилятор Borland C++ создает для каждого дальнего объекта отдельный сегмент. Параметры компилятора командной строки -zE, -zF и -zH (которые могут также задаваться директивой #pragma option) влияют на имя, класс и группу дальнего сегмента, соответственно. Изменяя эти значения при помощи указания #pragma option, вы тем самым распространяете новые установки на все объявления дальних объектов. Таким образом, для создания в конкретном сегменте дальнего объекта, можно использовать следующую последовательность:


            #pragma option -zEmysegment -zHmygroup -zFmyclass



            int far x;



            #pragma option -zE* =zH* -zF*





Тем самым x будет помещен в сегмент MYSEGMENT с классом 'MYCLASS' в группе 'MYGROUP', после чего все дальние объекты будут сброшены в значения, используемые по умолчанию. Отметим, что при использовании этих параметров можно поместить несколько дальних объектов в один сегмент:


            #pragma option -zEcombined -zFmyclass



            int far x;



            double far y;



            #pragma option -zE* -zF*





И x, и y окажутся в сегменте COMBINED 'MYCLASS', без группы.

Объявление ближних или дальних функций

В некоторых случаях вам может потребоваться переопределить заданное по умолчание значение типа функции для модели памяти. Например, вы используете модель памяти 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 */



            }





Теперь при компиляции вашей программы Borland C++ знает, что myputs ожидает указатель на char. Поскольку компиляция выполняется с моделью large, то известно, что указатель должен быть __far. Вследствие этого Borland 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). Более полную информацию об этих трех библиотечных функциях Borland C++ можно найти в справочнике по библиотеке.

Использование библиотечных файлов

Borland C++ предлагает для каждой из шести моделей памяти собственную версию библиотеки стандартных подпрограмм. Компилятор Borland C++ при этом проявляет достаточно "интеллекта", чтобы при последующей компоновке брать нужные библиотеки и в нужной последовательности, в зависимости от выбранной вами модели памяти. Однако, при непосредственном использовании компоновщика Borland C++ TLINK (как автономного компоновщика) вы должны явно указывать используемые библиотеки.

Компоновка смешанных модулей

Что произойдет, если вы компилируете один модуль с использованием модели памяти small (малая), второй - модели large (большая), и затем хотите скомпоновать их? Что при этом произойдет?

Файлы скомпонуются удовлетворительно, но при этом вы столкнетесь с проблемами. Если функция модуля с моделью small вызывает функцию в модуле с моделью large, она будет использовать при этом ближний вызов, что даст абсолютно неверные результаты. Кроме того, у вас возникнут проблемы с указателями, описанные в разделе "Объявление указателей как near, far или huge", поскольку функция в модуле small ожидает, что принимаемые и передаваемые ей указатели будут __near, тогда как функция в модуле large ожидает работу с указателями __far.

И снова решение заключается в использовании прототипов функций. Предположим, что вы поместили myputs в отдельный модуль и скомпилировали его с моделью памяти large. Затем вы создаете файл заголовка myputs.h (либо с любым другим именем и расширением .h), который содержит следующий прототип функции:


            void far myputs(char far *s);





Теперь, если поместить функцию main в отдельный модуль (MYMAIN.C) и выполнить следующие установки:


            #include <stdio.h>



            #include "myputs.h"





            main()



            {



               char near *mystr;



               mystr = "Hello, world\n";



               myputs(mystr);



            }





то при компиляции данной программы Borland C++ считает прототип функции из файла MYPUTS.H и увидит, что это функция __far, ожидающая указатель __far. В результате этого даже при модели памяти small при компиляции будет сгенерирован правильный вызывающий код.

Как быть, если помимо этого вам требуется компоновка с библиотечными подпрограммами? Лучший подход здесь заключается в том, чтобы выбрать одну из библиотек с моделью large и объявить все как far. Для этого сделайте копии всех файлов заголовка, которые вы обычно включаете (таких, как stdio.h) и переименуйте эти копии (например, fstdio.h).

Затем отредактируйте копии прототипов функций таким образом, чтобы там было явно указано far, например:


            int far cdecl printf(char far* format, ...);





Тем самым, не только вызовы подпрограмм будут дальними, но и передаваемые указатели также будут дальними. Модифицируйте вашу программу таким образом, чтобы она включала новый файл заголовка:


            #include <fstdio.h>





            main()



            {



               char near *mystr;



               mystr = "Hello, world\n";



               printf(mystr);



            }





Скомпилируйте вашу программу при помощи компилятора BCC, затем скомпонуйте ее при помощью утилиты TLINK, указав библиотеки с моделью памяти large, например CL.LIB. Смешивание модулей с разными моделями - вещь экстравагантная, но допустимая. Будьте, однако, готовы к тому, что любые неточности здесь приводят к ошибкам, которые очень трудно найти и исправить при отладке.

Назад | Содержание | Вперед

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

VDS хостинг Облачный сервер в Нидерландах и Украине

Аренда виртуального сервера от $7.91

Партнёрская программа
$20 за клиента

Wildcard сертификаты от $74,97 в год.

Дешевые ssl сертификаты для домена

Sectigo сертификаты от $7,67 в год.

хостинг Украина Виртуальный хостинг для сайта от $4,87

Регистрация домена от $2 в год

Партнерская программа – $20 за клиента

VPS с гибкой конфигурацией: за 1€

Мощные выделенные сервера: от 25€

Собственный Дата-Центр
Поддержка 24/7

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

Новости мира IT:

Архив новостей

Последние комментарии:

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...