Коли і для яких цілей слід використовувати ключове слово const в C для змінних?


58

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

Мене бентежить, які існують різні ситуації, коли це може бути корисним.

  • Чи слід його використовувати для наочності в прототипах функцій?
  • Чи слід його використовувати як захід безпеки під час розробки коду?
  • Чи слід його використовувати в межах різноманітних функцій для декларування констант часу виконання?
  • Чи варто його взагалі використовувати?

Ці питання - лише приклади плутанини, з якою я стикаюся. Загальна плутанина є

  • Коли має бути constключове слово, яке використовується у програмуванні на С?
  • Які існують різні типи переваг, які можна отримати за допомогою цього ключового слова в C?
  • Чи є якісь мінуси використання constключового слова?


Вказувалося, що це питання може бути занадто широким через усі ці питання в деталях мого запитання. Я просто хотів уточнити, що ці питання є лише для уточнення плутанини щодо основного питання.

Коли і для яких цілей слід використовувати ключове слово const в C для змінних?

Його також можна перефразовувати як

Правильне використання constключового слова в C` з плюсами і мінусами одного і того ж.


Для тих, хто голосує, його закрити, будь ласка, поясніть, чому він не підпадає під категорію " Specific issues with software development. Я досить конкретний.
Асеем Бансал

Я здогадуюсь, це тому, що ви задали кілька запитань в одному і тому самому дописі, і тому ваше запитання підпадає під категорію Занадто широкі: "Занадто багато можливих відповідей, або хороших відповідей буде занадто довго для цього формату. Будь ласка, додайте деталі щоб звузити набір відповідей або виділити питання, на яке можна відповісти в кількох абзацах. "
Роберт Харві

1
@RobertHarvey Всі ці питання лише для пояснення моєї плутанини. Єдиним моїм питанням залишається назва цього питання. Хороша відповідь на це охоплювала б усі ці пов’язані питання. Тож залишається а specific issue. Правильне використання constключового слова в C` з плюсами і мінусами одного і того ж.
Асеем Бансал

@RobertHarvey Чи краще зараз?
Асеем Бансал

Відповіді:


64

Переглядаючи код, я застосовую такі правила:

  • Завжди використовуйте constпараметри функції, передані посиланням, коли функція не змінює (або звільняє) вказані дані.

    int find(const int *data, size_t size, int value);
  • Завжди використовуйте constдля констант, які в іншому випадку можна визначити за допомогою #define або enum. У результаті компілятор може знайти дані в пам'яті лише для читання (ROM) (хоча лінкер часто є кращим інструментом для цієї мети у вбудованих системах).

    const double PI = 3.14;
  • Ніколи не використовуйте const у прототипі функції для параметра, переданого за значенням . Це не має жодного значення, а значить, просто "шум".

    // don't add const to 'value' or 'size'
    int find(const int *data, size_t size, const int value); 
  • Якщо потрібно, використовуйте const volatileмісця, які не можуть бути змінені програмою, але можуть все-таки змінитися. Типові випадки використання тут є регістри обладнання, наприклад, регістр стану, який відображає стан пристрою:

    const volatile int32_t *DEVICE_STATUS =  (int32_t*) 0x100;

Інші способи використання необов’язкові. Наприклад, параметри функції в рамках реалізації функції можуть бути позначені як const.

// 'value' and 'size can be marked as const here
int find(const int *data, const size_t size, const int value)  
{
     ... etc

або функціонувати повернені значення або обчислення, які отримуються, а потім ніколи не змінюються:

char *repeat_str(const char *str, size_t n) 
{
    const size_t len = strlen(str);
    const size_t buf_size = 1 + (len * n);
    char *buf = malloc(buf_size);
    ...

Ці використання constпросто означають, що ви не зміните змінну; вони не змінюють, як і де зберігається змінна. Звичайно, компілятор може виявити, що змінна не змінюється, але додавши constВи дозволяєте їй це застосувати. Це може допомогти читачеві та додати певну безпеку (хоча якщо ваші функції досить великі або складні, що це має велику різницю, у вас, певно, є інші проблеми). Редагувати - наприклад. 200-рядкова щільно кодована функція з вкладеними циклами та багатьма довгими або подібними назвами змінних, знаючи, що певні змінні ніколи не змінюються, може полегшити розуміння. Такі функції були погано розроблені або підтримувані.


Проблеми з const. Ви, ймовірно, почуєте термін "отруєння const". Це відбувається, коли додавання constдо параметра параметра викликає поширеність «constness».

Редагувати - отруєння const: наприклад у функції:

int function_a(char * str, int n)
{
    ...
    function_b(str);
    ...
}

якщо ми переходимо strдо цього const, ми повинні переконатися, що це fuction_bтакож займає const. І так далі, якщо function_bпереходить strна і function_cт. Д. Як ви можете уявити, це може бути болісно, ​​якщо воно поширюється на безліч окремих файлів / модулів. Якщо він поширюється на функцію, яку неможливо змінити (наприклад, системну бібліотеку), тоді необхідний додаток. Тож розсипання constіснуючого коду, можливо, вимагає неприємностей. Однак у новому коді найкраще constпослідовно класифікувати, де це доречно.

Більш підступною проблемою constє те, що це було не мовою оригіналу. Як доповнення він не зовсім підходить. Для початку це має два значення (як у правилах вище, тобто "я не збираюся цього змінювати" і "це неможливо змінити"). Але більше того, це може бути небезпечно. Наприклад, компілюйте та запустіть цей код і (залежно від компілятора / параметрів) він може збитися під час запуску:

const char str[] = "hello world\n";
char *s = strchr(str, '\n');
*s = '\0';

strchrповертає a char*не a const char*. Як його параметр виклику, constвін повинен передавати параметр виклику char*. І в цьому випадку це відкидає реальну властивість зберігання лише для читання. Редагувати: - це стосується, як правило, варіантів пам’яті лише для читання. Під «ROM» я маю на увазі не просто фізичний ПЗУ, а будь-яку пам'ять, захищену від запису, як це відбувається з кодовим розділом програм, що працюють у типовій ОС.

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


Це насправді не має двох значень. Це просто означає, як ви говорите, " я не збираюся цього змінювати". Наприклад, цілком справедливо мати const volatileзмінну.
детлей

2
Це стосується параметрів функції, але для констант в області файлу, наприклад, constзмінна дійсно є лише для читання: const" Неможливо змінити це". А const volatileз іншого боку говорить: "Це неможливо змінити, але воно може змінитися". Я додам згадку про летючі речовини до своєї відповіді.
Вільям Морріс

Я не погоджуюся з вашим коментарем, лише з думкою, що це інструкція компілятору поставити його в ПЗУ. Це має на увазі певний захист від невизначеної поведінки (тобто зміна констату через якийсь спосіб), що може призвести до непорозумінь на кшталт "чому я можу змінити цю constзмінну через не constвказівник" (поширене запитання щодо SO та кожного іншого форуму C !). Загалом, мені подобається ця відповідь :)
деттрал

Ви маєте рацію, "Помістіть це в ПЗУ" є неточним (хоча лаконічним :-) - я зміню його на "Це неможливо змінити", що є більш точним. Дякуємо за ваші коментарі.
Вільям Морріс

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

8

Як правило, в будь-якій мові програмування рекомендується використовувати constабо аналогічний модифікатор, оскільки

  • Він може уточнити абоненту, що те, що вони передали, не зміниться
  • Потенційні покращення швидкості, оскільки компілятор точно знає, що він може опустити певні речі, які є актуальними лише в тому випадку, якщо параметр може змінитися
  • Захист від себе випадково зміни цінності

1

Так, це в основному відповідь TheLQ.

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

У тому, що я зазвичай бачу, воно в основному використовується для додавання постійних значень у код і для вказівки, що масив чи структура не будуть змінені, якщо ви викликаєте певну функцію. Ця остання частина важлива, тому що, коли ви викликаєте функцію, яка ВИМОЖЛИ модифікує ваш масив чи структуру, можливо, ви захочете зберегти оригінальну версію, тож ви створите копію змінної та перекладете її до функції. Якщо це не так, копію, очевидно, не потрібно, тому, наприклад, ви можете змінити,

int foo(Structure s);

до

int foo(const Structure * s);

і не отримувати копію накладних витрат.

Додамо лише, зауважте, що C має особливі правила із специфікатором const. Наприклад,

int b = 1;
const int * a = &b;

не те саме, що

int b = 1;
int * const a = &b;

Перший код не дозволить вам змінити. У другому випадку вказівник є постійним, але його вміст - ні, тому компілятор дозволить вам сказати * a = 3;без помилки компілятора, але ви не можете зробити aпосилання на іншу річ.


0

Відповідно до заяв TheLQ:

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

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