Одна из причин, по которой стандарт C определяет вычитание только для двух указателей, если они находятся в одном и том же массиве, заключается в том, что некоторые (в основном старые) реализации C используют форму адресации, в которой адрес состоит из базы адрес плюс смещение , и разные массивы могут иметь разные базовые адреса.
На некоторых машинах полный адрес в памяти может иметь базу, которая представляет собой количество сегментов или другое блоки некоторого вида и смещение, которое является числом байтов на странице. Это было сделано потому, что, например, некоторые ранние аппаратные средства работали с данными в 16-битных фрагментах и были предназначены для работы с 16-битными адресами, но более поздние версии аппаратного обеспечения, расширяющие ту же архитектуру, будут иметь более крупные адреса, но все равно будут использовать 16-битные адреса. кусочки данных, чтобы сохранить некоторую совместимость с предыдущим программным обеспечением. Таким образом, новое оборудование может иметь 22-разрядное адресное пространство. Старое программное обеспечение, использующее только 16-разрядные адреса, все равно будет вести себя одинаково, но более новое программное обеспечение может использовать дополнительный фрагмент данных для указания различных базовых адресов и, таким образом, получать доступ ко всей памяти в 22-разрядном адресном пространстве.
В таких случаях система, комбинация базы b и смещения o может относиться к адресу памяти 64 • b + o . Это дает доступ к полному 22-битному адресному пространству - с b = 65535 и o = 63 мы имеем 64 • b + o = 64 • 65535 + 63 = 4,194,303 = 2 22 * 1026 * -1.
Обратите внимание, что многие местоположения в форме могут быть доступны по нескольким адресам. Например, b = 17, o = 40 относится к тому же местоположению, что и b = 16, o = 104 и как b = 15, o = 168. Хотя формула для создания 22-битного адреса могла бы быть рассчитана на 65536 • b + o , и это дало бы каждой ячейке памяти уникальный адрес, перекрывающаяся формула используется, потому что это дает программисту гибкость в выборе их базы. Напомним, что эти машины были разработаны с использованием 16-битных фрагментов данных. При использовании схемы с неперекрывающимися адресами вам придется вычислять как основание, так и смещение при выполнении арифметики адреса c. С помощью схемы с перекрывающимися адресами вы можете выбрать базу для массива, с которым вы работаете, и затем выполнение любого арифметического адреса c требует вычисления только с смещенной частью.
Реализация C для этой архитектуры может легко поддерживать массивы до 65536 массивов, устанавливая один базовый адрес для массива, а затем выполняя арифметику c только со смещенной частью. Например, если у нас есть массив A
1000 int
, и он размещается, начиная с ячейки памяти 78 976 (что равно 1234 • 64), мы можем установить b в 1234 и индексировать массив со смещением от 0 до 1998 (999 • 2, поскольку каждый int
составляет два байта в этой реализации C).
Затем, если у нас есть указатель p
, указывающий на A[125]
, он обозначается с помощью (1234, 250), чтобы указать на смещение 250 с основанием 1234. А если q
указывает на A[55]
, то оно представляется с помощью (1234, 110). Чтобы вычесть эти указатели, мы игнорируем основание, вычитаем смещения и делим на размер одного элемента, поэтому получается (250-110) / 2 = 70.
Теперь, если у вас есть указатель r
, указывающий на элемент 13 в некотором другом массиве B
, он будет иметь другую базу, скажем, 2345. Таким образом, r
будет представлен с помощью (2345, 26). Затем, чтобы вычесть r
из p
, нам нужно вычесть (2345, 26) из (1234, 250). В этом случае вы не можете игнорировать базы; простая работа со смещениями даст (250-26) / 2 = 112, но эти элементы не находятся на расстоянии 112 элементов (или 224 байта).
Компилятор может быть изменен для выполнения математических операций путем вычитания оснований , умножив на 64, и прибавьте это к разности смещений. Но затем он делает математику, чтобы вычесть указатели, которые совершенно не нужны в предполагаемом использовании арифметики указателей c. Таким образом, стандартный комитет C решил, что компилятор не должен поддерживать это, и способ указать, что поведение не определено, когда вы вычитаете указатели на элементы в разных массивах.