Что такое ближний, дальний и огромный указатели? - PullRequest
27 голосов
/ 26 августа 2010

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

Ответы [ 6 ]

28 голосов
/ 26 августа 2010

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

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

Решением Intel было создание 16-разрядных «регистров сегментов», содержимое которых было бы смещено влево на четыре бита идобавлен в адрес.Например:

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

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

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

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

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

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

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

20 голосов
/ 05 ноября 2013

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

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

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

Указатели

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

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

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

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

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

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

Рассмотрим следующий пример, взятый из ЗДЕСЬ :

 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. Дальний указатель никогда не нормализуется, но указатель 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 равно 1234:1234 и h1 равно 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

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

12 голосов
/ 26 августа 2010

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

3 голосов
/ 26 августа 2010

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

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

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

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

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

Это вернет нас в до, о, о, о,

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

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

2 голосов
/ 26 августа 2010

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

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

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

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

2 голосов
/ 26 августа 2010

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

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

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

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

Тем не менее, более крупные программы хотели бы иметь дело с более чем 64 КБ непрерывных данных. ОГРОМНЫЙ указатель выглядит точно так же, как дальний указатель - он имеет 32-битное хранилище - но распределитель позаботился об упорядочении диапазона сегментов данных с последовательными идентификаторами, чтобы путем простого увеличения селектора сегмента данных можно было получить следующий фрагмент данных размером 64 КБ достиг.

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

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