Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

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

Это создало концепцию сегмента 64 kiB. Таким образом, "ближайший" указатель будет просто содержимым регистра DX (5678h) и будет недействительным, если регистр DS не будет установлен правильно, а "дальний" указатель - 32 бита (12345678h, DS, затем DX) и всегда работал (но был медленнее, поскольку вам приходилось загружать два регистра, а затем восстанавливать регистр DS, когда это делается).

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

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

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

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

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

Ответ 3

Разница между дальними и большими указателями:

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

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

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

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

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

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

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

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), но они не считаются равными в первом случае, поскольку в случае Far указателей сравниваются только смещения, т.е. проверяется, будет ли 0001==0041. Это неверно.

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

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

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

Ответ 4

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

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

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

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

tee: напиток с вареньем и хлебом.

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

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

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

Ответ 5

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

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

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

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

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

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

Ответ 6

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

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

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

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