Код, подобный приведенному выше, эффективно стремится использовать C как форму «ассемблера высокого уровня». Хотя некоторые люди настаивают на том, что C не является ассемблером высокого уровня, авторы Стандарта C сказали это в своем опубликованном документе Rationale:
Хотя он стремился дать программистам возможность писать
По-настоящему переносимые программы, Комитет C89 не хотел заставлять программистов писать переносимо, чтобы исключить использование C в качестве «высокоуровневого ассемблера»: способность писать машинный код - одна из сильных сторон языка C. этот принцип в значительной степени мотивирует проведение различия между строго соответствующей программой и соответствующей программой (§4).
Стандарты C и C ++ сознательно избегают требования, чтобы все реализации были пригодны для использования в качестве ассемблеров высокого уровня, и не пытаются определить все поведения, необходимые для того, чтобы сделать их подходящими для таких целей. Следовательно, поведение таких конструкций, как ваша, которые эффективно рассматривают компилятор как высокоуровневый ассемблер, не определяется Стандартом. Однако авторы Стандарта явно признают ценность способности некоторых программ использовать язык в качестве высокоуровневого ассемблера и, таким образом, явно предполагают, что такой код, как ваш, может использоваться в реализациях, предназначенных для поддержки таких конструкций, - сбой определение поведения никоим образом не подразумевает, что такой код «сломан».
Еще до того, как был написан стандарт, реализации, предназначенные для низкоуровневого программирования на платформах, где было бы целесообразно обрабатывать преобразования между указателями и целыми числами одинакового размера, просто интерпретируя их биты, по существу, единодушно обрабатывали бы такие преобразования таким образом. Такая обработка значительно облегчает низкоуровневое программирование на таких платформах, но авторы Стандарта не видят причин для этого. На платформах, где такое поведение не имеет смысла, такой мандат будет вредным, а на тех, где это имеет смысл, разработчики компиляторов будут вести себя соответствующим образом с этим или без него, что делает его ненужным.
К сожалению, авторы Стандарта были слишком самонадеянны. Опубликованное Обоснование заявляет о желании поддерживать Дух C, принципы которого включают «Не мешайте программисту делать то, что нужно сделать». Это может означать, что на платформе с естественным упорядочением памяти может потребоваться область хранения, которая в разное время «принадлежит» разным контекстам выполнения, - качественная реализация, предназначенная для низкоуровневого программирования на такой платформе, учитывая что-то вроде:
extern volatile uint8_t buffer_owner;
extern volatile uint8_t * volatile buffer_address;
buffer_address = buffer;
buffer_owner = BUFF_OWNER_INTERRUPT;
... buffer might be asynchronously written at any time here
while(buffer_owner != BUFF_OWNER_MAINLINE)
{ // Wait until interrupt handler is done with the buffer and...
} // won't be accessing it anymore.
result = buffer[0];
должен прочитать значение из buffer[0]
после код прочитал object_owner
и получил значение BUFF_OWNER_MAINLINE
. К сожалению, некоторые реализации думают, что было бы лучше попытаться использовать некоторое ранее наблюдавшееся значение buffer[0]
, чем рассматривать изменчивый доступ как возможное освобождение и повторное приобретение права собственности на рассматриваемое хранилище.
В общем, компиляторы будут надежно обрабатывать такие конструкции с отключенными оптимизациями (и фактически будут делать это с volatile
) или без него, но не смогут эффективно обрабатывать такой код без использования специфических для компилятора директив (что также приведет к volatile
ненужно). Я думаю, что дух C должен прояснить, что качественные компиляторы, предназначенные для низкоуровневого программирования, должны избегать оптимизаций, которые ослабили бы семантику volatile
таким образом, чтобы низкоуровневые программисты могли делать то, что может понадобиться на цели платформа, но, видимо, это не совсем понятно.