Что такое ближние, дальние и огромные указатели?

кто-нибудь может объяснить мне эти указатели с подходящим примером ... а когда используются эти указатели?

6 ответов


в старые времена, согласно руководству Turbo C, Ближний указатель был всего лишь 16 бит, когда весь ваш код и данные вписывались в один сегмент. Указатель far состоял из сегмента, а также смещения, но нормализация не проводилась. И огромный указатель был автоматически нормализован. Два дальних указателя могут указывать на одно и то же место в памяти, но быть разными, тогда как нормализованные огромные указатели, указывающие на одно и то же место в памяти, всегда будут равны.


основным примером является архитектура Intel X86.

Intel 8086 был, внутренне, 16-разрядным процессором: все его регистры были 16 битами в ширину. Однако адресная шина была шириной 20 бит (1 MiB). Это означало, что вы не могли держать весь адрес в реестре, ограничивая вас первыми 64 кибами.

решение Intel заключалось в создании 16-битных "сегментных регистров", содержимое которых будет смещено влево на четыре бита и добавлено к адресу. Для пример:

DS ("Data Segment") register:  1234 h
DX ("D eXtended") register:   + 5678h
                              ------
Actual address read:           179B8h

это создало концепцию сегмента 64 КБ. Таким образом, "рядом" указатель будет содержимое DX в регистр (5678h), и будут считаться недействительными, если ДС регистр уже был установлен правильно, а "дальний" указатель 32 бит (12345678h, ДС последующим DX) и будет всегда работать (но медленнее, поскольку вы должны загрузить два реестра, а затем восстановить реестр, когда ДС сделали).

(Как отмечает supercat ниже, смещение к DX, которое переполнено "перевернись"до добавляется в DS, чтобы получить окончательный адрес. Это позволило 16-битным смещениям получить доступ к любому адресу в сегменте 64 кб, а не только к той части, которая была ± 32 кб от точки DX, как это делается в других архитектурах с 16-битной относительной смещенной адресацией в некоторых инструкциях.)

однако обратите внимание, что у вас могут быть два "далеких" указателя, которые имеют разные значения, но указывают на один и тот же адрес. Например, указатель far 100079B8h указывает на то же самое место как 12345678h. Таким образом, сравнение указателей на дальних указателях было недопустимой операцией: указатели могли отличаться, но все равно указывать на одно и то же место.

именно здесь я решил, что Macs (с процессорами Motorola 68000 в то время) были не так уж плохи, поэтому я пропустил огромные указатели. IIRC, они были просто указателями far, которые гарантировали, что все перекрывающиеся биты в регистрах сегментов были 0, как во втором примере.

у Motorola этого не было проблема с их 6800 сериями процессоров, так как они были ограничены 64 КБ, когда они создавали архитектуру 68000, они шли прямо к 32-битным регистрам и, таким образом, никогда не нуждались в ближних, дальних или огромных указателях. (Вместо этого их проблема заключалась в том, что только нижние 24 бита адреса действительно имели значение, поэтому некоторые программисты (как известно, Apple) использовали высокие 8 бит в качестве "флагов указателей", вызывая проблемы, когда адресные шины расширялись до 32 бит (4 гиб).)

Линус Торвальдс просто протянули время до 80386, которые предлагает "защищенный режим", где были адреса 32 бит, и в сегментные регистры были высокие половине адресу, и без того была необходима, и писал Линукс с самого начала, чтобы использовать защищенный режим, ни странно сегмента вещи, и поэтому у вас нет ближнего и Дальнего указателя поддержки в Linux (и почему ни одна компания разрабатывает новую архитектуру когда-нибудь к ним вернуться, если они хотят поддержки Linux). И они ели менестрелей Робина, и их было много. веселье. (Ура...)


разница между далекими и огромными указателями:

как мы знаем, по умолчанию указатели near например: int *p это near указатель. Размер near указатель на 2 байта в случае 16-битного компилятора. И мы уже очень хорошо знаем, что размер варьируется от компилятора к компилятору; они хранят только смещение адреса указателя, на который он ссылается. Адрес, состоящий только из смещения, имеет диапазон 0-64K байт.

Far и huge указатели:

Far и huge указатели имеют размер 4 байта. Они хранят как сегмент, так и смещение адреса, на который ссылается указатель. Тогда что такое разница между ними?

ограничение Дальнего указателя:

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

если вы увеличите дальний адрес сверх максимального значения его смещенного адреса вместо увеличения адреса сегмента, он повторит свой смещенный адрес в циклическом порядке. Это также называется обертыванием, т. е. если смещение 0xffff и мы добавим 1 тогда 0x0000 и аналогично, если мы уменьшим 0x0000 к 1 тогда 0xffff и помните, что в сегменте нет изменений.

теперь я собираюсь сравнить огромные и далекие указатели:

1.Когда указатель far увеличивается или уменьшается только смещение указателя фактически увеличивается или уменьшается, но в случае огромного указателя сегмент и значение смещения будут меняться.

рассмотрим следующий пример, взятый из здесь :

 int main()
    {
    char far* f=(char far*)0x0000ffff;
    printf("%Fp",f+0x1);
    return 0;
  }

тогда выход:

0000:0000

значение сегмента не изменяется.

и в случае огромные указатели:

int main()
{
char huge* h=(char huge*)0x0000000f;
printf("%Fp",h+0x1);
return 0;
}

выход:

0001:0000

это из-за операции приращения не только значение смещения, но и значение сегмента также изменяется.Это означает, что сегмент не изменится в случае far указатели, но в случае huge указатель, он может перемещаться из одного сегмента в другой .

2.Когда реляционные операторы используются на дальних указателях, сравниваются только смещения.Другими словами, реляционные операторы будут работать только на дальних указателях, если значения сегментов сравниваемых указателей одинаковы. И в случае огромного этого не произойдет, на самом деле происходит сравнение абсолютных адресов.Давайте разберемся с помощью примера far указатели :

int main()
{
char far * p=(char far*)0x12340001;
char far* p1=(char far*)0x12300041;
if(p==p1)
printf("same");
else
printf("different");
return 0;
}

выход:

different

на huge указатели :

int main()
{
char huge * p=(char huge*)0x12340001;
char huge* p1=(char huge*)0x12300041;
if(p==p1)
printf("same");
else
printf("different");
return 0;
}

выход:

same

пояснение: как мы видим абсолютный адрес, так как p и p1 и 12341 (1234*10+1 или 1230*10+41), но они не считаются равными в 1-м случае, потому что в случае far указатели только смещения сравниваются, т. е. он будет проверять, является ли 0001==0041. Это ложь.

и в случае огромных указателей операция сравнения выполняется по абсолютным адресам, которые равны.

  1. указатель far никогда не нормализуется, но huge указатель нормализуется . Нормализованный указатель-это тот, который имеет как можно больше адреса в сегменте, что означает это смещение не превышает 15.

    предположим, если у нас есть 0x1234:1234 тогда нормализованная форма его 0x1357:0004(абсолютный адрес 13574). Огромный указатель нормализуется только тогда, когда на нем выполняется некоторая арифметическая операция, а не нормализуется при назначении.

     int main()
     {
      char huge* h=(char huge*)0x12341234;
      char huge* h1=(char huge*)0x12341234;
      printf("h=%Fp\nh1=%Fp",h,h1+0x1);
      return 0;
     }
    

    выход:

    h=1234:1234
    
    h1=1357:0005
    

    объяснение:huge указатель не нормализуется в случае назначения.Но если на нем будет выполнена арифметическая операция, то это будет нормализуется.Так, h is 1234:1234 и h1 is 1357:0005что нормализуется.

    4.Смещение огромного указателя меньше 16 из-за нормализации и не так в случае дальних указателей.

    давайте рассмотрим пример, чтобы понять, что я хочу сказать :

     int main()
      {
      char far* f=(char far*)0x0000000f;
      printf("%Fp",f+0x1);
      return 0;
      }
    

выход:

    0000:0010

в случае huge указатели :

      int main()
      {
      char huge* h=(char huge*)0x0000000f;
        printf("%Fp",h+0x1);
        return 0;
        }

        Output:
        0001:0000

объяснение:поскольку мы увеличиваем указатель far на 1, он будет 0000:0010.И как мы увеличиваем огромный указатель на 1, то он будет 0001:0000 потому что смещение не может быть больше 15, другими словами, оно будет нормализовано.


все вещи в этом ответе относятся только к старой модели сегментированной памяти 8086 и 80286.

рядом: 16-битный указатель, который может адресовать любой байт в сегменте 64k

far: 32-битный указатель, содержащий сегмент и смещение. Обратите внимание, что поскольку сегменты могут перекрываться, два разных указателя far могут указывать на один и тот же адрес.

huge: 32-битный указатель, в котором сегмент "нормализуется", так что никакие два дальних указателя не указывают на то же самое адрес, если они не имеют одинаковое значение.

tee: напиток с джемом и хлебом.

Это вернет нас к doh oh oh oh

а когда используются эти указатели?

в 1980 - х и 90-х годах, пока 32-битные окна не стали вездесущими,


эта терминология использовалась в 16-битных архитектурах.

в 16-битных системах данные были разделены на сегменты 64Kb. Каждый загружаемый модуль (программный файл, динамически загружаемая библиотека и т. д.) имел связанный сегмент данных, который мог хранить только до 64 КБ данных.

Ближний указатель был указателем с 16-битным хранилищем и ссылался на данные (только) в текущем сегменте данных модулей.

16bit программы, которые имели более 64 КБ данных в качестве требования можно получить доступ к специальным распределителям, которые будут возвращать указатель FAR-идентификатор сегмента данных в верхних 16 битах и указатель на этот сегмент данных в нижних 16 битах.

еще большие программы хотели бы иметь дело с более чем 64Kb непрерывных данных. Огромный указатель выглядит точно так же, как дальний указатель - он имеет 32-битное хранилище - но распределитель позаботился о том, чтобы организовать диапазон сегментов данных с последовательными идентификаторами, так что, просто увеличив селектор сегмента данных, далее 64 КБ данных может быть достигнута.

базовые стандарты языка C и c++ никогда официально не признавали эти понятия в своих моделях памяти - все указатели в программе C или c++ должны быть одинакового размера. Таким образом, ближние, дальние и огромные атрибуты были расширениями, предоставляемыми различными поставщиками компиляторов.


в некоторых архитектурах, указатель, который может указывать на любой объект в системе будет больше и медленнее работать, чем тот, который может указывать на полезное подмножество вещи. Многие люди дали ответы, связанные с 16-разрядной архитектурой x86. Различные типы указателей были распространены в 16-битных системах, хотя в 64-битных системах могут появиться различия вблизи / страха, в зависимости от того, как они реализованы (я не удивлюсь, если многие системы разработки перейдут на 64-битные указатели для все, несмотря на то, что во многих случаях это будет весьма расточительно).

во многих программах довольно легко подразделить использование памяти на две категории: небольшие вещи, которые вместе составляют до довольно небольшого количества вещей (64K или 4GB), но будут доступны часто, и большие вещи, которые могут составлять до гораздо большего количества, но которые не должны быть доступны так часто. Когда приложению необходимо работать с частью объекта в области "большие вещи", оно копирует эту часть в область "мелочи", работает с ней, а при необходимости записывает ее обратно.

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

(Примечание: даже на многих 32-разрядных системах некоторые области памяти могут быть доступны напрямую без дополнительных инструкций, в то время как другие области не могут. Если, например, на 68000 или руке, один держит регистр, указывающий на глобальное хранилище переменных, можно будет напрямую загрузить любую переменную в пределах первых 32K (68000) или 2K (ARM) этого регистра. Получение переменной, хранящейся в другом месте, потребует дополнительной инструкции для вычисления адреса. Размещение более часто используемых переменных в предпочтительных регионах и информирование компилятора позволят более эффективно генерировать код.