From: kmeaw
Date: 2026-05-15 08:45:15Z
Интересно, ведь это лишает компилятор возможности делать более сложные
оптимизации (const) и после них генерировать правильный код (volatile).
Вырожденный пример для выдуманного микроконтроллера:
/* tied to a HW register by the linker */
extern volatile uint64_t OSCCTL;
extern volatile uint64_t TMR0CNT;
#define OSCCTL_UNLOCK_STEPS 4
const uint64_t OSCCTL_UNLOCK[OSCCTL_UNLOCK_STEPS] = {
0x11111110,
0x22222220,
0x33333330,
0x44444440,
};
#define OSCCTL_LOWPWR 0x80000000
#define OSCCTL_BUSY 0x00000001
void lowpower() {
size_t i;
for (i = 0; i < OSCCTL_UNLOCK_STEPS; ++i) {
OSCCTL |= OSCCTL_UNLOCK[i];
while (OSCCTL & OSCCTL_BUSY);
}
OSCCTL |= OSCCTL_LOWPWR;
while (OSCCTL & OSCCTL_BUSY);
}
void msleep(uint64_t interval) {
/* FIXME: handle timer overflows and tiny intervals */
uint64_t now = TMR0CNT;
uint64_t later = now + interval;
while (TMR0CNT < later) {
lowpower();
}
}
const тут позволяет компилятору развернуть цикл и заинлайнить ключи.
volatile запретит кеширование аппаратного регистра в регистрах общего
назначения, без этого оптимизатор мог бы доказать, что цикл в msleep
бесконечный, а в lowpower BUSY никогда не случается, да и вообще
дооптимизироваться до OSCCTL |= 0xC7777770 - эту запись он выкинуть уже не
имеет права, потому что переменная не объявлена static, и может использоваться
в соседних единицах трансляции.