Вы нашли правильный раздел стандарта, этот код приводит к неопределенному поведению.
Функция, записывающая что-то «на аппаратное обеспечение», вероятно, должна иметь параметр volatile
-qualifier, в зависимости от того, что такое «аппаратное обеспечение».Если это регистр с отображением в памяти, буфер DMA или энергонезависимая память, то этот параметр определенно должен был иметь значение volatile unsigned char*
(или, необязательно, volatile uint8_t*
, что также следует рассматривать как символьный тип).
Подробности: C позволяет перебирать любой фрагмент данных с помощью символьного указателя, C17 6.3.2.3/7:
Когда указатель на объект преобразуется в указательна тип символа результат указывает на младший адресуемый байт объекта.Последовательные приращения результата, вплоть до размера объекта, дают указатели на оставшиеся байты объекта.
Часть, которую вы цитируете о доступе к «lvalue», относится к доступу к данным через другойтип указателя, чем то, что на самом деле хранится в этом месте.Проще говоря: независимо от того, сколько вы используете различных указателей, указывающих на него, фактические данные сохраняют свой первоначальный тип.
Доступ к данным через неверный тип указателя обычно даже не разрешен, но опять-таки доступ к символам является особым исключениемк «правилу строгого алиасинга», C17 6.5 / 7:
Доступ к сохраненному значению объекта должен быть доступен только через выражение lvalue, которое имеет один из следующих типов:
...
- тип символа.
Таким образом, вы можете получить доступ к любому виду данных через указатель символа, но если этот указатель не квалифицирован как volatile, вы вызываете неопределенное поведение согласно части, которую вы указали,C17 6.7.3 / 5.
На практике использование энергонезависимого указателя может привести к тому, что компилятор неожиданно оптимизирует доступ.Так что это не просто теоретическая «языковая адвокатура», на практике вы можете получить очень странный код, сгенерированный с включенной оптимизацией.Множество очень трудных для поиска ошибок во встроенных системах происходят из-за такого пропущенного volatile
.
Что касается вашего дополнительного вопроса, приведение и buffer + 3
ничего не изменят: вы все еще имеете дело ссимвольный указатель без квалификатора volatile
- того же типа.Фактические данные остаются типа volatile unsigned char
, поэтому вы не можете получить к ним доступ из функции через unsigned char*
.