Різниця між const і const volatile


89

Якщо ми оголошуємо змінну як volatileкожен раз, коли оновляється свіже значення
Якщо ми оголошуємо змінну як, constтоді значення цієї змінної не буде змінено

Тоді const volatile int temp;
яка користь від оголошення змінної tempяк вище?
Що станеться, якщо ми оголосимо як const int temp?


Ви б не використовували const volatile int temp;в області блоку (тобто всередині { }), він там не має ніякої користі.
MM

Відповіді:


145

Об'єкт, позначений як const volatile, заборонено змінювати кодом (помилка буде викликана через constкваліфікатор) - принаймні через це конкретне ім'я / покажчик.

volatileЧастина Класифікатора означає , що компілятор не може оптимізувати або змінити порядок доступу до об'єкта.

У вбудованій системі це зазвичай використовується для доступу до апаратних регістрів, які можуть бути прочитані та оновлені апаратним забезпеченням, але немає сенсу писати (або це може бути помилкою для запису).

Прикладом може бути регістр стану послідовного порту. Різні біти вказують, чи символ чекає на читання, чи регістр передачі готовий прийняти новий символ (тобто, - він порожній). Кожне зчитування цього реєстру стану може спричинити різне значення залежно від того, що ще відбулося в апаратному забезпеченні послідовного порту.

Немає сенсу писати в регістр стану (залежно від конкретної технічної специфікації), але потрібно переконатися, що кожне зчитування реєстру призводить до фактичного зчитування апаратного забезпечення - використовуючи кешоване значення попереднього виграного зчитування ' не розповім вам про зміни в апаратному стані.

Швидкий приклад:

unsigned int const volatile *status_reg; // assume these are assigned to point to the 
unsigned char const volatile *recv_reg;  //   correct hardware addresses


#define UART_CHAR_READY 0x00000001

int get_next_char()
{
    while ((*status_reg & UART_CHAR_READY) == 0) {
        // do nothing but spin
    }

    return *recv_reg;
}

Якщо ці вказівники не позначені як такі volatile, може виникнути пара проблем:

  • тест циклу while може прочитати регістр стану лише один раз, оскільки компілятор міг припустити, що все, на що він вказував, ніколи не зміниться (у тесті циклу while або в самому циклі немає нічого, що могло б це змінити). Якщо ви ввели функцію, коли в апаратному забезпеченні UART не було символів, що чекають, ви можете потрапити в нескінченний цикл, який ніколи не зупинявся навіть при отриманні символу.
  • читання реєстру отримання може бути переміщено компілятором до циклу while - знову ж таки, оскільки у функції немає нічого, що вказує на те *recv_reg, що цикл змінив, немає жодної причини, що його неможливо прочитати перед входом у цикл.

В volatileкласифікатори гарантує , що ці оптимізації не виконуються компілятором.


5
+1 для пояснення. І у мене виникає запитання: а як щодо методів const volatile? Якщо у мене є клас, до якого доступ багато потоків (хоча доступ синхронізується з mutex), чи мої методи const також повинні бути мінливими (оскільки якусь змінну можна змінити іншим потоком)
Саша

39
  • volatile скаже компілятору не оптимізувати змінну, пов'язану з кодом, зазвичай, коли ми знаємо, що її можна змінити ззовні, наприклад іншим потоком.
  • const скаже компілятору, що програмі заборонено змінювати значення змінної.
  • const volatileце дуже особлива річ, яку ви, мабуть, побачите вживати рівно 0 разів у своєму житті (tm). Як і слід було очікувати, це означає, що програма не може змінити значення змінної, але значення може бути змінено ззовні, отже, ніяких оптимізацій змінної не буде здійснено.

12
Я б подумав, що volatileзмінні - це зазвичай те, що відбувається, коли ти починаєш возитися з обладнанням, а не з іншими потоками. Те, що я бачив, const volatileвикористовується в таких речах, як відображені в пам'яті реєстри стану тощо.
ТІЛЬКИ МОЙ правильний ДУМКА

2
Звичайно, ви абсолютно праві, багатопотоковість - це лише один приклад, але не єдиний :).
мінго

25
Якщо ви працюєте із вбудованими системами, ви дуже часто це побачите.
Даніель Грілло,

28

Це не тому, що змінна const, можливо, вона не змінилася між двома точками послідовності.

Constness - це обіцянка, яку ви даєте не змінювати значення, не те, що значення не буде змінено.


9
Плюс один за вказівку на те, що constдані не є "постійними".
Богдан Александру

7

Мені потрібно було використовувати це у вбудованому додатку, де деякі змінні конфігурації знаходяться в області флеш-пам'яті, яка може бути оновлена ​​завантажувачем. Ці змінні конфігурації є "постійними" під час виконання, але без мінливого кваліфікатора компілятор оптимізував би щось подібне ...

cantx.id = 0x10<<24 | CANID<<12 | 0;

... шляхом попереднього обчислення значення константи та використання негайної інструкції з монтажу або завантаження константи з сусіднього місця, щоб будь-які оновлення вихідного значення CANID в області спалаху конфігурації були проігноровані. CANID повинен бути const мінливим.


7

У мові C const та volatile є типовими кваліфікаторами, і ці два незалежні.

В основному, const означає, що програма не може змінювати значення.

А волатильність означає, що вартість може змінюватися раптово (можливо, поза межами програми).

Насправді, стандарт C згадує приклад дійсної декларації, яка є одночасно і const, і нестійкою. Прикладом є

“Extern const volatile int real_time_clock;”

де real_time_clock може бути змінено апаратно, але не може бути призначений, збільшений або зменшений.

Отже, ми вже повинні ставитися до const і volatile окремо. Крім того, цей типовий кваліфікатор застосовується також до struct, union, enum і typedef.


5

Ви можете використовувати const і volatile разом. Наприклад, якщо 0x30 вважається значенням порту, яке змінюється лише зовнішніми умовами, наступна декларація запобіжить будь-якій можливості випадкових побічних ефектів:

const volatile char *port = (const volatile char *)0x30;

4

constозначає, що змінна не може бути змінена кодом c, не те, що вона не може змінюватися. Це означає, що жодна інструкція не може писати у змінну, але її значення все одно може змінитися.

volatileозначає, що змінна може змінюватися в будь-який час і, отже, не можна використовувати кешовані значення; кожен доступ до змінної повинен виконуватися за адресою пам'яті.

Оскільки питання позначено тегом "вбудовано" і припустимо temp, що це змінна, оголошена користувачем, а не апаратно-пов'язаний регістр (оскільки вони зазвичай обробляються в окремому файлі .h), розглянемо:

Вбудований процесор, який має як енергонезалежну пам'ять даних для читання-запису (ОЗУ), так і енергонезалежну пам'ять, доступну лише для читання, наприклад FLASH-пам'ять у архітектурі фон-Неймана, де дані та простір програм мають спільну шину даних та адреси.

Якщо ви заявите, const tempщо має значення (принаймні, якщо воно відрізняється від 0), компілятор призначить змінну адресі в просторі FLASH, оскільки навіть якщо вона була призначена адресі оперативної пам'яті, вона все одно потребує пам'яті FLASH для зберігання початкового значення змінної, роблячи адресу оперативної пам'яті марною тратою місця, оскільки всі операції доступні лише для читання.

Як наслідок:

int temp;- змінна, що зберігається в оперативній пам'яті, ініціалізована до 0 при запуску (cstart), можуть використовуватися кешовані значення.

const int temp;- це змінна, що зберігається у (read-ony) FLASH, ініціалізована до 0 під час компілятора, можуть використовуватися кешовані значення.

volatile int temp; - це змінна, яка зберігається в оперативній пам'яті, ініціалізована до 0 при запуску (cstart), кешовані значення НЕ будуть використовуватися.

const volatile int temp; - це змінна, що зберігається у (read-ony) FLASH, ініціалізована до 0 під час компілятора, кешовані значення НЕ будуть використовуватися

Ось корисна частина:

На сьогоднішній день більшість вбудованих процесорів мають можливість вносити зміни до своєї енергонезалежної пам'яті, доступної лише для читання, за допомогою спеціального функціонального модуля, в цьому випадку const int tempйого можна змінити під час виконання, не безпосередньо. Зазначене іншим чином, функція може модифікувати значення за адресою, де tempзберігається.

Практичним прикладом може бути використання tempсерійного номера пристрою. Під час першого запуску вбудованого процесора tempбуде дорівнює 0 (або заявленому значенню), і функція може використовувати цей факт для запуску тесту під час виробництва, і якщо це вдало, попросіть присвоїти серійний номер і змінити значення tempза допомогою особливої ​​функції. Деякі процесори мають спеціальний діапазон адрес з OTP (одноразово програмованою) пам'яттю саме для цього.

Але тут різниця:

Якщо const int tempмодифікуваний ідентифікатор замість одноразово програмованого серійного номера і НЕ оголошений volatile, кешоване значення може бути використано до наступного завантаження, тобто новий ідентифікатор може бути недійсним до наступного перезавантаження або, що ще гірше, деяких функцій може використовувати нове значення, тоді як інше може використовувати старіше кешоване значення до перезавантаження. Якщо const int tempIS оголошено voltaile, зміна посвідчення набере чинності негайно.


Ого, ця відповідь довга


2

Говорячи простими словами, значення змінної 'const volatile' не може бути змінено програмно, але може бути змінено апаратно. Нестабільність тут полягає у запобіганні будь-якій оптимізації компілятора.


1

Ми використовуємо ключове слово const для змінної, коли ми не хочемо, щоб програма її змінювала. Тоді як, коли ми оголошуємо змінну "const volatile", ми говоримо програмі не змінювати її та компілятору, що ця змінна може бути несподівано змінена із вхідних даних із зовнішнього світу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.