Стандарт не требует, чтобы компиляторы вели себя предсказуемо, если доступ к объекту типа "int" осуществляется с использованием lvalue, которое не имеет видимой связи с этим типом. В обосновании, однако, авторы отмечают, что классификация некоторых действий как неопределенного поведения предназначена для того, чтобы позволить рынку решать, какое поведение считается необходимым в качественных реализациях. В целом, действие по преобразованию указателя в другой тип и последующему немедленному выполнению доступа к нему относится к категории действий, которые будут поддерживаться качественными компиляторами, которые настроены для использования в системном программировании, но могут не поддерживаться компиляторами. которые действуют тупо.
Однако, даже игнорируя проблему типа lvalue, Стандарт не предъявляет никаких требований в отношении того, что происходит, если приложение пытается прочитать из памяти, которой оно не владеет. Здесь опять выбор поведения может иногда быть проблемой качества реализации. Здесь есть пять основных возможностей:
В некоторых реализациях содержимое хранилища может быть
предсказуемо с помощью средств, не описанных в стандарте, и читать
выдаст содержимое такого хранилища.
Акт чтения может вести себя так, как будто он дает биты с
Неуказанные значения, но не имеют другого побочного эффекта.
Попытка чтения может завершить программу.
На платформах, использующих сопоставленный с памятью ввод-вывод, чтение за пределами допустимого диапазона
выполнить неожиданную операцию с неизвестными последствиями; этот
возможность применима только на определенных платформах.
Реализации, которые пытаются быть «умными» различными способами, могут пытаться
сделать выводы, основываясь на том, что чтение не может произойти, таким образом
что приводит к побочным эффектам, которые выходят за рамки законов времени и причинности.
Если вы знаете, что ваш код будет работать на платформе, где чтение имеет
никаких побочных эффектов, реализация не будет пытаться быть «умной», и ваш код
подготовлен для любого шаблона битов, которые может дать чтение, затем под этими
обстоятельства такого чтения могут иметь полезное поведение, но вы будете ограничивать
ситуации, когда ваш код может быть использован.
Обратите внимание, что в то время как реализации, которые определяют __STDC_ANALYZABLE__
, требуются
заставить большинство действий подчиняться законам времени и причинности даже в тех случаях, когда
Стандарт не налагает никаких других требований, за пределами чтения
классифицируется как критическое неопределенное поведение и поэтому должно рассматриваться
опасно для любой реализации, в которой прямо не указано иное.
Кстати, на некоторых платформах есть еще одна проблема, которая может возникнуть, даже если, например, код использовал int[3]
вместо одного int
: выравнивание. На некоторых платформах значения определенных типов могут считываться или записываться только на определенные адреса, а некоторые адреса, которые подходят для меньших типов, могут не подходить для больших. На платформах, где int
требуется 32-битное выравнивание, но double
требует 64-битное выравнивание, учитывая int foo[3]
, компилятор может произвольно поместить foo
, так что (double*)foo
будет подходящим адресом для хранения double
или так, чтобы (double*)(foo+1)
было подходящим местом. Программист, знакомый с деталями реализации, может определить, какой адрес будет действительным, и использовать его, но код, который слепо предполагает, что адрес foo
будет действительным, может завершиться ошибкой, если double
имеет 64- требование выравнивания битов.