В чем разница между дальними и ближними указателями? - PullRequest
46 голосов
/ 17 ноября 2009

Может кто-нибудь сказать мне разницу между far указателями и near указателями в C?

Ответы [ 6 ]

42 голосов
/ 17 ноября 2009

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

  • DS → сегмент данных
  • CS → кодовый сегмент
  • SS → сегмент стека
  • ES → дополнительный сегмент

Логический адрес этой архитектуры записан segment:offset. Теперь ответим на вопрос:

  • Ближайшие указатели относятся (как смещение) к текущему сегменту.

  • Дальние указатели используют информацию о сегментах и ​​смещение, чтобы указывать на сегменты. Таким образом, чтобы использовать их, DS или CS должны быть изменены на указанное значение, память будет разыменована, а затем восстановлено исходное значение DS / CS. Обратите внимание, что арифметика указателей на них не изменяет сегментную часть указателя, поэтому переполнение смещения просто обернет его вокруг.

  • И затем есть огромные указатели, которые нормализованы, чтобы иметь максимально возможный сегмент для данного адреса (в отличие от дальних указателей).

В 32-разрядных и 64-разрядных архитектурах модели памяти используют сегменты по-разному или не используют их вообще.

34 голосов
/ 11 января 2016

Поскольку никто не упомянул DOS, давайте забудем о старых компьютерах с DOS-ПК и посмотрим на это с общей точки зрения. Тогда, очень упрощенно, это выглядит так:


Любой ЦП имеет шину данных, которая представляет собой максимальный объем данных, которые ЦП может обработать в одной инструкции, т. Е. Равный размеру его регистров. Ширина шины данных выражается в битах: 8 бит, или 16 бит, или 64 бита и т. Д. Отсюда и происходит термин «64-битный ЦП» - он относится к шине данных.

Любой ЦП имеет адресную шину, также с определенной шириной шины, выраженной в битах. Любая ячейка памяти вашего компьютера, к которой центральный процессор может обращаться напрямую, имеет уникальный адрес. Адресная шина достаточно велика, чтобы охватить всю имеющуюся адресную память.

Например, если у компьютера есть 65536 байтов адресуемой памяти, вы можете покрыть их 16-битной адресной шиной, 2 ^ 16 = 65536.

Чаще всего, но не всегда, ширина шины данных равна ширине адресной шины. Хорошо, если они имеют одинаковый размер, так как это делает набор инструкций ЦП и программы, написанные для него, более четкими. Если процессору необходимо вычислить адрес, удобно, если этот адрес достаточно мал, чтобы поместиться в регистры процессора (часто называемые индексными регистрами, когда речь идет об адресах).

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

Например, для ЦП с 16-битной шиной данных может быть удобно иметь 16-битную адресную шину. Но этому же компьютеру может потребоваться более 2 ^ 16 = 65536 байт = 64 КБ адресуемой памяти.

В этом случае процессор обычно будет иметь специальные инструкции (которые немного медленнее), что позволяет ему обращаться к памяти за пределами этих 64 КБ. Например, ЦП может разделить свою большую память на n страниц (также иногда называемых банки , сегментов и другие подобные термины, которые могут означать другое вещь из одного процессора в другой), где каждая страница составляет 64 КБ. Затем он будет иметь «страничный» регистр, который должен быть установлен первым, прежде чем обращаться к этой расширенной памяти. Точно так же у него будут специальные инструкции при вызове / возврате из подпрограмм в расширенной памяти.

Чтобы компилятор C генерировал правильные инструкции ЦП при работе с такой расширенной памятью, были изобретены нестандартные ключевые слова near и far. Нестандартные, как в, они не определены стандартом C, но они де-факто являются отраслевым стандартом, и почти каждый компилятор в некотором роде поддерживает их.

far относится к памяти, расположенной в расширенной памяти, за пределами ширины адресной шины. Поскольку это относится к адресам, чаще всего вы используете его при объявлении указателей. Например: int * far x; означает «дай мне указатель, который указывает на расширенную память». И тогда компилятор будет знать, что он должен генерировать специальные инструкции, необходимые для доступа к такой памяти. Точно так же указатели функций, использующие far, будут генерировать специальные инструкции для перехода в / из расширенной памяти. Если бы вы не использовали far, вы бы получили указатель на обычную адресуемую память и в итоге указали бы на что-то совершенно другое.

near в основном включено для соответствия с far; это относится к чему-либо в адресуемой памяти, как эквивалентно обычному указателю. Таким образом, это в основном бесполезное ключевое слово, за исключением редких случаев, когда вы хотите убедиться, что код помещен в стандартную адресуемую память. Затем вы можете явно пометить что-то как near. Наиболее типичным случаем является низкоуровневое аппаратное программирование, когда вы пишете процедуры обслуживания прерываний. Они вызываются аппаратно из вектора прерывания с фиксированной шириной, которая равна ширине адресной шины. Это означает, что подпрограмма обработки прерываний должна находиться в стандартной адресуемой памяти.


Самым известным использованием far и near является, возможно, упомянутый старый ПК с MS DOS, который в настоящее время считается довольно древним и поэтому представляет небольшой интерес.

Но эти ключевые слова существуют и на более современных процессорах! Это особенно заметно во встроенных системах, где они существуют практически для каждого семейства микроконтроллеров 8 и 16 бит, представленных на рынке, поскольку эти микроконтроллеры обычно имеют ширину адресной шины 16 бит, но иногда больше 64 КБ памяти.

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

Одной из основных причин, по которой возникла тенденция к разработке 64-битного ПК, было то, что 32-битные ПК достигли точки, когда их использование памяти начало достигать предела адресной шины: они могли адресовать только 4 ГБ оперативной памяти 2 ^ 32 = 4,29 млрд байт = 4 Гб. Чтобы можно было использовать больше оперативной памяти, можно было либо прибегнуть к какому-нибудь обременительному решению с расширенной памятью, как в дни DOS, либо расширить компьютеры, включая их адресную шину, до 64 бит.

22 голосов
/ 17 ноября 2009

Дальние и ближние указатели использовались на старых платформах, таких как DOS.

Я не думаю, что они актуальны в современных платформах. Но вы можете узнать о них здесь и здесь (как указано в других ответах). По сути, указатель far - это способ расширения адресуемой памяти компьютера. Т.е., адрес более 64 КБ памяти на 16-битной платформе.

4 голосов
/ 29 января 2012

Указатель в основном содержит адреса. Как мы все знаем, управление памятью Intel разделено на 4 сегмента. Таким образом, когда адрес, на который указывает указатель, находится в том же сегменте, тогда это ближний указатель, и поэтому для его смещения требуется всего 2 байта. С другой стороны, когда указатель указывает на адрес, который находится вне сегмента (то есть в другом сегменте), тогда этот указатель является дальним указателем. Он состоит из 4 байтов: два для сегмента и два для смещения.

0 голосов
/ 11 января 2016

Ну, в DOS было довольно забавно иметь дело с регистрами. И сегменты. Все о максимальных счетных объемах оперативной памяти.

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

С тех пор, как win nt4 (когда они украли идеи у * nix), программисты Microsoft начали использовать так называемые пространства памяти пользователя / ядра. И избегал прямого доступа к физическим контроллерам с тех пор. С тех пор исчезла проблема, связанная и с прямым доступом к сегментам памяти. - Все стало R / W через ОС.

Однако, если вы настаиваете на понимании и манипулировании дальними / близкими указателями, посмотрите на источник ядра Linux и как он работает - я думаю, вы вернетесь новее.

И если вам все еще нужно использовать CS (сегмент кода) / DS (сегмент данных) в DOS. Посмотрите на это:

https://en.wikipedia.org/wiki/Intel_Memory_Model http://www.digitalmars.com/ctg/ctgMemoryModel.html

Я хотел бы указать на идеальный ответ ниже ... от Лундина. Мне было лень отвечать правильно. Лундин дал очень подробное и разумное объяснение «палец вверх»!

0 голосов
/ 08 января 2013

Четыре регистра используются для ссылки на четыре сегмента в 16-битной архитектуре с сегментной памятью x86. DS (сегмент данных), CS (сегмент кода), SS (сегмент стека) и ES (дополнительный сегмент). Логическим адресом на этой платформе является сегмент: смещение в шестнадцатеричном формате.

Ближайшие указатели относятся (как смещение) к текущему сегменту.

Дальние указатели используют информацию о сегментах и ​​смещение, чтобы указывать на сегменты. Таким образом, чтобы использовать их, DS или CS должны быть изменены на указанное значение, память будет разыменована, а затем восстановлено исходное значение DS / CS. Обратите внимание, что арифметика указателей на них не изменяет сегментную часть указателя, поэтому переполнение смещения просто обернет его вокруг.

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

В 32-разрядных и 64-разрядных архитектурах модели памяти используют сегменты по-разному или не используют их вообще.

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