Рейтинг: 5/5
Чи стикалися ви під час роботи з Сі або Сі ++ кодом з наступними ситуаціями?
- код, який прекрасно працює, поки ви не включите оптимізацію
- код, який прекрасно працює, поки заборонені переривання
- завдання ОСРВ, які чудово працюють в ізоляції, поки не створена якась інша задача
Якщо ви відповіли «так» на одне з цих тверджень, швидше за все ви не використовуєте ключове слово volatile. Ви не самотні. Використання цього специфікатор погано вивчено багатьма програмістами, оскільки більшість книг з програмування на мові Сі не приділяють цій темі належної уваги.
Ключове слово volatile пишеться до або після типу даних оголошується змінної.
volatile intfoo;
int volatilefoo;
Покажчики на volatile змінні оголошуються так.
volatile int * pReg;
int volatile * pReg;
Volatile покажчики на НЕ volatile змінні зустрічаються вкрай рідко (думаю, я використовував їх лише один раз), але я про всяк випадок дам їх синтаксис:
int * volatile p;
І, для повноти, якщо вам знадобиться volatile покажчик на volatile змінну, слід написати:
int volatile * volatile p;
Якщо ви використовуєте volatile для структури (struct) або об'єднання (union), дія специфікатор буде поширюватися на весь вміст структури / об'єднання. Якщо ви не хочете цього, то можете застосувати специфікатор volatile до окремих елементів структури / об'єднання.
Правильне використання специфікатор VOLATILE
Мінлива повинна бути оголошена з ключовим словом volatile щоразу, коли її значення може змінитися несподівано.
uint8_t * pReg = (uint8_t *) 0x1234;
// Wait for register to become non-zero
В цьому випадку, майже напевно буде збій, як тільки ви включите оптимізацію. Компілятор згенерує асемблерний код подібний цьому:
Логічне обгрунтування оптимізатора досить просте: вже прочитавши значення змінної в акумулятор (другу рядок коду), немає необхідності зчитувати його заново, оскільки значення завжди буде тим же. Таким чином, в третьому рядку ми опинимося в нескінченному циклі. Щоб змусити компілятор зробити те, що нам потрібно, ми змінимо опис на:
uint8_t volatile * pReg = (uint8_t volatile *) 0x1234;
Асемблерний код тепер виглядає наступним чином:
Необхідний хід процесу досягнуто.
Невловимі проблеми можуть виникати в регістрах, мають специфічні характеристики. Наприклад, безліч периферійних пристроїв містять регістри, які очищаються при простому їх прочитанні. Кілька додаткових причетний понад ваших намірів (або ж навпаки, менше, ніж ви планували) можуть привести в цих випадках до несподіваних результатів.
Стосовно до програмування мікроконтролерів AVR, немає ніякої необхідності у використанні покажчиків на периферійні регістри, тому що код в цьому випадку виходить дуже громіздким. Pashgan.
Програма обробки переривання (ISR)
Обробники переривання часто встановлюють значення змінних, які перевіряються в основній частині коду. Наприклад, переривання послідовного порту може перевіряти кожен отриманий символ, щоб дізнатися, чи є він символом кінця рядка (імовірно позначає кінець повідомлення). Якщо символ є кінцем рядка, обробник переривань може встановити глобальний прапор. Невірна реалізація в цьому випадку може бути такий:
int etx_rcvd = FALSE;