volatile не нужно только тогда, когда значение объекта полностью
под контролем текущего процесса в контексте текущей страницы
исходного кода. Если значение может изменяться неожиданно для
процесса (как ниже заметили - DMA, прерывания, регистр периферии),
то, в таком случае, для гарантии предсказуемости поведения кода,
требуется модификатор volatile. Если старые компиляторы могли
подразумевать необходимость принудительного чтения значения по
указателю, то у современных, за чередой оптимизаций, разница между обращениями к переменной по имени или по указателю - размыта полностью. Забыл volatile - сам себе злобный буратино.
Ткнуть носом ТС не могу - лень искать цитаты стандартов и интерпритировать их для ТС, это его собственное упражнение.
Практически, при обращении к большому массиву по указателю, он перечитывается. Но, если у вас в коде, возможны не последовательные обращения к отдельным элементам массива, то оптимизация может оставить буферизированные значения в регистрах общего назначения, не перечитывая ОЗУ.
Если жмут ресурсы и есть необходимость оптимизации, подумайте о повторном использовании буферов. К примеру, при нехватке памяти, я использовал кучу. Да, это не быстро, но позволяло использовать один и тот же объём ОЗУ для разных буферов несколько раз. Другой вариант - использовать союзы (union), но, по сравнению с выделением места в куче, вариант более требовательный к внимательности программиста.