Модификаторы типов указателей


Turbo С имеет семь модификаторов типа указателей на дaнные: near, far, huge, _cs, _ds, _еs, _ss.

Указатели могут быть представлены в двух формах: "близ­кие", или nеаг-указатeлu и "далекие", или fаг-указатeлu.

"Близкий" указатель задает только смещение адреса объ­екта, на который указатель ссылается. В качестве сегментной части адреса объекта используется текущее значение сегмен­тнoгo регистра DS. Поэтому nеаг-указатель занимает в памя­ти компьютера только два байта и позволяет адресовать не более 64 К байт. "Дaлeкий" указатель задает полный адрес объекта: значе­ние сегмента и значение смещения. Поэтому fа.г-указатель занимает в памяти компьютера 4 байта и позволяет адресо­ватъ любой байт в адресном пространстве.

Первое слово, рас­сматриваемое как число без знака, задает смещение, второе ­значение сегмента адреса. При доступе к объекту данных по значению указателя (т. е. при выполнении операции *) сег­мент адреса помещается в сегментный регистр (как правило, это регистр ES). Частный случай fаг-указателя - это huge­-указатель, занимающий также 4 байта.

Формат указателя, задаваемый по умолчанию, может быть переопределен явным указанием формы представления. Для этого при описании указателя используются модифика­торы near, far и huge. Например:

iпt nеаг ** ptr; char far * strings, huge * norm_ptr;

Явное переопределение формы указателя с far по умолча­нию на near позволяет получать более компактный код про­граммы, но применимо только для программ, полный объем дaнныx которых не превышает 64 Кбайт. В противном случае требуются дополнителъныe усилия по переопределению cer­ментного peгиcтpa DS или использование особых форм near­-указателя _еs, _ds, _сs, _ss. ­

Использование fаг-указателей позволяет обращаться к любому байту адресного пространства компьютера. Весьма популярное применение fаг-указателя – непосредственный доступ к памяти видеоадаптера или специальным областям BIOS.

Пример

Дается простой пример определения типа архитектуры персоналъного компьютера (известно, что пред­последний байт памяти по физическому адресу FFFFEh (ПЗУ ВIOS) содержит индикатор архитектуры персональногo компьютера (значение FFh соответствует компьютеру IВM РС, FEh - компьютеру IВM РС ХТ, FDh - компьютеру IВM PCjr, FCh - компьютеру IВM РС АТ):

#include

void main (void) ­

{

char * messages[] = {"IBM РС", "IBM РС ХТ',

"IВM PCjr", "IВM РС АТ', "не опознан!",};

char far *ptr = (char far* )0xF000FFFEL;

index = * рtr ^ 0xFF;

printf("Tun комnьютера, сообщаемый BIОS: %s\n",

index <= 3 ? messages[index] : messages[ 4]);

}

Использование far-указателей таит в себе несколько «под­вoдных камней». Первый из них - многoзначность представле­ния одного и того же физического адреса памяти. Существует множество пар <сегмент:смещение>, соответствующих одному и тому же адресу в памяти. Например, все три указателя

char far * ptr = 0xb8000000L;

char far * ptr2 = 0xb4004000L;

сhaг far * рtrЗ = 0xb0008000L;

соответствуют одному и тому же физическому адресу - адресу начала видеопамяти цветных адаптеров при их работе в режи­мах 0 - 6. Однако сравнение указателей друг с другом опера­цией == дacт всегда значение ложь. При выполнении срав­нения faг-указателей операциями >=, >, <=, < используются только смещения, т.е. младшие слова указателей в формате unsigned. Поэтому, например, ptrl < ptr2 дает значение ис­тина, а ptr2 >= ptr3 - ложь. Сравнение же far-указателей операциями != и == выполняется как сравнение чисел long unsigned, а не как сравнение значений физических адресов.

Другая важная проблема использования указателей, в том числе и fаг-указателей, известна в программировании как "перескакивание сегмента" (segment round wrapping). Если к указателю прибавляется какое-то число, изменяется только значение смещения. Если при этом полученное значение сме­щения должно превысить FFFFh, "перенос" в значение сег­мента не происходит, а смещение "перепрыгивает" вновь на границу 0000. Например, если к fаг-указателю 0400:FF00h прибавить lFFh, получается значение 0400: 00FFh, а не1400:00FFh, как можно было бы ожидать. Для nеаr-указате­ля, равного FFF0h, прибавление 30h дает значение 0020h.

От этих проблем избавлены указатели типа huge. Их часто называют нормализованными. Это означает, что:

1) указатель представляется единственной комбинацией значений <сегмент:смещение>. При этом смещение имеет ми­нимально возможное значение, а сегмент - максимально воз­можное. Отсюда следует, что значение смещения заключено в диапазоне от 0 дo Fh. Например, физическому адресу па­мяти FF0FFh соответствует нормализованный указатель FF0F:000Fh. Как следствие этого, операция сравнения == или != выполняется корректно в том смысле, что равные указате­ли соответствуют равным адресам;

2) при выполнении арифметических операций автомати­чески восстанавливается нормализация указателя. При вы­полнении операций сравнения >, >=, < и <= используются все 32 бита hugе-указателя. Это гарантирует корректный резуль­тат сравнения. Автоматическая нормализация hugе-указате­ля при выполнении арифметических операций над указате­лями позволяет адресовать из С-программы блоки данных размером более 64К байт.

Пример

В качестве примера использования hugе-указателя при­ведем программу вычисления суммы всех слов области данных BIOS, начиная с адреса F000:0000h. Эга сумма уни­кальна для серии компьютеров конкретного производителя, архитектуры и состава внешних устройств и может исполь­зоваться для самоопределения пpoгpaммы по аппаратуре, например для защиты программ от несанкционированного копирования.

#include

void main ()

{

unsigned hugе *ptr = (unsigned huge* )0xF0000000L;

long unsigned bios_sum = 0;

/* Цuкл пока указатель не paвeн 0000:0000 * /

while( ptr ) bios_sum += * ptr++;

printf("Cyмма кодов ВIОS данного компьютера"\

"( 16с/с) %08х\n", bios_sum);

}

 

Платой за удобство использования hugе-указателей яв­ляется значительное замедление скорости работы программы из-за того, что адресная арифметика выполняется специ­альными функциями.

Особый тип nеаr-указателя в Turbo С - это указатели типа _ds, _еs, _ss и _cs. Такие указатели воздейст­вуют на правила, которые используются компилятором при генерации машинного кода программы. Например, для обыч­ного nеаr-указателя компилятор, встретив операцию «*», гене­рирует мaшиннyю инструкцию, в которой для доступа к данным используется регистр DS, а значение указателя задает только смещение. Для указателя _es компилятор сгенерирует машин­ную инструкцию с префиксом переопределения сегмента, и при формировании физического адреса данных процессор будет использовать текущее значение в ES. Для указателя _СS при формировании физического адреса используется регистр CS и т.д. Естественно, что такие указатели могут использоваться, если установлено нужное значение сегментного регистра. Для доступа к сегментным и другим внутренним регистрам про­цессора можно использовать псевдопеременные _CS, _SS, _ES, _DS и т. п., присваивая им соответствующие значения или их считывая. Отметим, что использование указателей с моди­фикаторами _ds, _es, _ss и _cs требует большой осторожности и опыта.

Пример

В качестве простейшего примера использования указате­лей с подобными модификаторами приведем намного более производительный, чем предыдущий, вариант программы опреде­ления суммы всех слов о6ласти данных BIOS:

#include

void main (void)

{

unsigned _es * ptr = (unsigned _es *) 0х0000;

long unsigned bios_sиm = 0;

_ES = 0xF000;

do{ do

bios_sum += * ptr++;

while( ptr); /* пока адрес не равен ES:0 */

_ES += 0хl000;

}while(_ES); /* пока ES не станет равным нулю */

pintf("Cумма кoдoв ВIOS данного компьютера "\

"( 16с/ с) %08х\n", bios_sum);

}

Приведенная программа более чем в 6 раз превосходит по скорости ранее рассмотренный вариант с использованием huge-­указателя. Однако еще раз подчеркнем, что работа с указателя­ми типа _ds, _es, _ss и _cs, равно как и доступ к регистрам процессора через псевдопеременные, требует большой осто­рожности. Часто значения регистров, в том числе и сегмент­ных, используемых указателями типа _ds, _es, _ss и _cs, изменяются неявно при выполнении операций С-прогpaммы.



Дата добавления: 2016-05-26; просмотров: 1759;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

Поделитесь с друзьями:

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.012 сек.