Какие формы адресного пространства памяти были использованы?
Сегодня широко распространено большое плоское виртуальное адресное пространство. Исторически использовались более сложные адресные пространства, такие как пара базового адреса и смещения, пара номера сегмента и смещения, адрес слова плюс некоторый индекс для байта или другого подобъекта и т. Д. ,
Время от времени в различных ответах и комментариях утверждается, что указатели C / C ++ являются по существу целыми числами. Это неверная модель для C / C ++, поскольку разнообразие адресных пространств, несомненно, является причиной некоторых правил C относительно операций с указателями. Например, отсутствие определения арифметики указателей вне массива упрощает поддержку указателей в базовой и смещенной модели. Ограничения на преобразование указателей упрощают поддержку моделей «адрес плюс дополнительные данные».
Это повторяющееся утверждение мотивирует этот вопрос. Я ищу информацию о разнообразии адресных пространств, чтобы проиллюстрировать, что указатель C / C ++ не обязательно является простым целым числом и что ограничения C / C ++ для операций с указателями разумны, учитывая широкое разнообразие поддерживаемых машин.
Полезная информация может включать в себя:
Это широкий вопрос, поэтому я открыт для предложений по управлению им. Я был бы рад видеть совместное редактирование одного и того же всеобъемлющего ответа. Тем не менее, это может не дать репутацию заслуженным. Я предлагаю внести несколько полезных вкладов.
Почти все, что вы можете себе представить, возможно, было использовано.
первое основное разделение между байтовой адресацией (все современные
архитектуры) и адресация слов (до IBM 360 / PDP-11, но
Я думаю, что современные мейнфреймы Unisys все еще адресуются). В
адресация слов, char*
а также void*
часто будет больше, чем
int*
; даже если бы они не были больше, «селектор байтов» был бы в старших битах, которые должны были быть 0, или
будет игнорироваться для всего, кроме байтов. (На PDP-10,
например, если p
был char*
, (int)p < (int)(p+1)
было бы
часто быть ложным, даже если int
а также char*
было то же самое
размер.)
Среди машин с байтовой адресацией основные варианты сегментированы
и несегментированные архитектуры. Оба по-прежнему широко распространены
сегодня, хотя в случае с Intel 32bit (сегментированный
архитектура с 48-битными адресами), некоторые из
используемые ОС (Windows и Linux) искусственно ограничивают пользователя
обрабатывает в один сегмент, имитируя плоскую адресацию.
Хотя у меня нет недавнего опыта, я бы ожидал даже больше
Разнообразие встроенных процессоров. В частности, в прошлом это
было часто для встроенных процессоров использовать Гарвард
архитектура, где код и данные были в независимом адресе
пробелы (так что указатель на функцию и указатель данных, приведенный к
достаточно большой интегральный тип, мог бы сравниться равным).
Я бы сказал, что вы задаете не тот вопрос, кроме исторического любопытства.
Даже если ваша система использует плоское адресное пространство — даже если каждая система с настоящего момента и до конца времени использует плоское адресное пространство — вы все равно не можете рассматривать указатели как целые числа.
Стандарты C и C ++ оставляют все виды арифметики указателей «неопределенными». Это может повлиять на вас прямо сейчас, в любой системе, потому что компиляторы предполагают, что вы избегаете неопределенного поведения и соответственно оптимизируете.
Для конкретного примера, три месяца назад в Valgrind появилась очень интересная ошибка:
http://comments.gmane.org/gmane.comp.debugging.valgrind.devel/19698
(Поиск «неопределенное поведение».)
По сути, Вальгринд использовал указатели «меньше» и «больше», чтобы попытаться определить, находится ли автоматическая переменная в определенном диапазоне. Поскольку сравнения между указателями в разных агрегатах «неопределены», Clang просто оптимизирован все из сравнений, чтобы вернуть константу true (или false; я забыл).
Эта ошибка сама породила интересный вопрос StackOverflow.
Таким образом, хотя первоначальные арифметические определения указателей могли быть ориентированы на реальные машины, и это само по себе может быть интересно, на самом деле это не имеет отношения к программированию сегодня. Сегодня важно то, что вы просто не можете предположить, что указатели ведут себя как целые числа, точка, независимо от системы, которую вы используете. «Неопределенное поведение» не означает «что-то смешное происходит»; это означает, что компилятор может предположить, что вы этим не занимаетесь. Когда вы это делаете, вы вводите противоречие в рассуждения компилятора; и из противоречия вытекает все что угодно … Это зависит только от того, насколько умен ваш компилятор.
И они становятся умнее все время.