У меня есть много беспорядка при понимании различия между "далеким" указателем и "огромным" указателем, искал все это в Google для решения, couldnot находят тот. Может любой объяснять меня различие между двумя. Кроме того, что точное понятие нормализации связано с огромными указателями.
donot дают мне следующее или любые подобные ответы:
"Единственная разница между далеким указателем и огромным указателем - то, что огромный указатель нормализован компилятором. Нормализованный указатель является тем, который имеет как можно больше адреса в сегменте, означая, что смещение никогда не больше, чем 15. Огромный указатель нормализован только, когда адресная арифметика с указателями выполняется на нем. Это не нормализовано, когда присвоение сделано. Можно заставить это быть нормализованным, не изменяя значение путем постепенного увеличения и затем постепенного уменьшения его. Смещение должно быть меньше чем 16, потому что сегмент может представить любое значение, больше, чем, или равняться 16 (например, Абсолютный адрес
0x17
в нормализованной форме был бы0001:0001
. В то время как далекий указатель мог обратиться к абсолютному адресу0x17
с0000:0017
, это не допустимый огромный (нормализованный) указатель, потому что смещение больше, чем0000F
.). Огромные указатели могут также быть увеличены и постепенно уменьшили арифметические операторы использования, но так как они нормализованы, они не перенесутся как далекие указатели."
Здесь понятие нормализации очень хорошо не объяснено или может быть, я не могу понять это очень хорошо.
Может любой пытаться объяснить это понятие с точки зрения новичков.
Спасибо, Rahamath
Вначале 8086 был расширением 8-битного процессора 8085. 8085 мог адресовать только 65536 байт с помощью своей 16-битной адресной шины. Когда Intel разработала 8086, они хотели, чтобы программное обеспечение было как можно более совместимым со старыми 8-битными процессорами, поэтому они представили концепцию сегментированной адресации памяти. Это позволяло запускать 8-битное программное обеспечение, чтобы жить в большем диапазоне адресов, не замечая этого. 8086 имел 20-битную адресную шину и, таким образом, мог обрабатывать до 1 МБ памяти (2 ^ 20). К сожалению, он не мог напрямую обращаться к этой памяти, для этого пришлось использовать сегментные регистры. Реальный адрес был вычислен путем добавления значения 16-битного сегмента, сдвинутого на 4 влево, к 16-битному смещению.
Example:
Segment 0x1234 Offset 0x5678 will give the real address
0x 1234
+0x 5678
---------
=0x 179B8
Как вы могли заметить, эта операция не является биективной, то есть вы можете сгенерировать реальный адрес с другими комбинациями сегмента и смещения.
0x 1264 0x 1111
+0x 5378 +0x 68A8
--------- --------- etc.
=0x 179B8 =0x 179B8
Фактически существует 4096 различных возможных комбинаций из-за 3 перекрывающихся полубайтов ( 3 * 4 = 12
бит, 2 ^ 12 = 4096
).
Нормализованная комбинация - единственная из 4096 возможных значений, у которой 3 старших полубайта смещения будут равны нулю.В нашем примере это будет:
0x 179B
+0x 0008
---------
=0x 179B8
Разница между далеким
и огромным
указателем не в нормализации, вы можете иметь ненормализованный огромный
указатель , это абсолютно разрешено. Разница заключается в коде, генерируемом при выполнении арифметических операций с указателями. С дальними указателями при увеличении или добавлении значений к указателю не будет обработки переполнения, и вы сможете обрабатывать только 64 КБ памяти.
char far *p = (char far *)0x1000FFFF;
p++;
printf("p=%p\n");
напечатает 1000: 0000
Для огромных указателей компилятор сгенерирует код, необходимый для обработки переноса.
char huge *p = (char huge *)0x1000FFFF;
p++;
printf("p=%p\n");
напечатает 2000: 0000
Это означает, что вы должны быть осторожны при использовании дальних или больших указателей, поскольку арифметические операции с ними различаются.
Также не следует забывать, что у большинства 16-битных компиляторов были библиотеки, которые не справлялись с этими случаями правильно, что иногда приводило к ошибкам в программном обеспечении.
Компилятор реального режима Microsoft не обрабатывал огромные указатели на все свои строковые функции. Borland был еще хуже, поскольку даже функции mem ( memcpy
, memset
и т. Д.) Не обрабатывали переполнения смещения. Это была причина, по которой было хорошей идеей использовать нормализованные указатели с этими библиотечными функциями, вероятность переполнения смещения для них была ниже.
Прежде всего необходимо понять, как сегментированный указатель преобразуется в линейный адрес. Для вашего примера преобразование выглядит следующим образом:
linear = segment * 16 + offset;
Из-за этого оказывается, что один и тот же линейный адрес может быть выражен с использованием различных комбинаций сегмент / смещение. Например, все следующие комбинации сегмент / смещение относятся к одному и тому же линейному адресу:
0004:0000
0003:0010
0002:0020
0001:0030
0000:0040
Проблема в том, что если у вас есть ptr1 с сегментированным адресом 0100: 0000
и ptr2 с сегментированным адресом из 0010: 0020
, простое сравнение определит, что ptr1! = ptr2
, даже если они фактически указывают на один и тот же адрес.
Нормализация - это процесс преобразования адреса в такую форму, при которой, если два ненормализованных указателя относятся к одному и тому же линейному адресу, они оба будут преобразованы в одну и ту же нормализованную форму.
Насколько я помню, это примерно так:
Если вы новичок, вероятно, лучше забыть о том, что вы слышали о Ближнем / Дальнем / Огромном. Они имеют значение только в старой 16-битной модели сегментированной памяти, которая обычно использовалась в ранних процессорах Intel 80x86. В 32- и 64-битных странах (то есть во всем, что было с 1994 года) память - это просто большой непрерывный блок, поэтому указатель - это просто указатель (для отдельного приложения).