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

59

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

Я не понимаю, какие существуют ситуации, когда это может быть полезно.

  • Должно ли оно использоваться для ясности в прототипах функций?
  • Должно ли оно использоваться в качестве меры безопасности при разработке кода?
  • Должно ли оно использоваться в рамках различных функций для объявления констант во время выполнения?
  • Должен ли он использоваться вообще?

Эти вопросы являются лишь примерами путаницы, с которой я сталкиваюсь. Общая путаница

  • Когда следует использовать constключевое слово в программировании на C?
  • Каковы различные типы преимуществ, которые могут быть получены с помощью этого ключевого слова в C?
  • Есть ли минусы использования constключевого слова?


Было отмечено, что этот вопрос может быть слишком широким из-за всех этих вопросов в деталях моего вопроса. Я просто хотел уточнить, что эти вопросы просто чтобы прояснить путаницу в отношении основного вопроса.

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

Это также можно перефразировать как

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

Aseem Bansal
источник
Для тех, кто голосует, чтобы он был закрыт, пожалуйста, объясните, почему он не подпадает под категорию Specific issues with software development. Я весьма конкретен.
Aseem Bansal
Я предполагаю, что это потому, что вы задали несколько вопросов в одном посте, и поэтому ваш вопрос подпадает под категорию слишком широких: «Слишком много возможных ответов, или хорошие ответы будут слишком длинными для этого формата. Пожалуйста, добавьте детали сузить набор ответов или выделить проблему, на которую можно ответить в нескольких параграфах. "
Роберт Харви
1
@RobertHarvey Все эти вопросы только для того, чтобы объяснить мою путаницу. Моим единственным вопросом остается название этого вопроса. Хороший ответ на этот вопрос охватывает все эти вопросы. Так и осталось specific issue. Правильное использование constключевого слова в C` с плюсами и минусами одинаково.
Aseem Bansal
@RobertHarvey Теперь лучше?
Aseem Bansal

Ответы:

65

При просмотре кода я применяю следующие правила:

  • Всегда используйте constдля параметров функции, передаваемых по ссылке, когда функция не изменяет (или не освобождает) указанные данные.

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

    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к параметру функции вызывает распространение «constness».

Редактировать - постоянное отравление: например, в функции:

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возвращает char*не const char*. Поскольку его параметр вызова - constэто, он должен привести параметр вызова к char*. И в этом случае это отбрасывает реальное свойство хранения только для чтения. Изменить: - это обычно относится к переменным в памяти только для чтения. Под «ПЗУ» я имею в виду не только физическое ПЗУ, но и любую память, защищенную от записи, как это происходит с разделом кода программ, работающих на типичной ОС.

Многие стандартные библиотечные функции ведут себя одинаково, поэтому будьте осторожны: когда у вас есть реальные константы (т.е. хранятся в ПЗУ), вы должны быть очень осторожны, чтобы не потерять их константу.

Уильям Моррис
источник
На самом деле это не имеет двух значений. Это просто означает, как вы говорите: « Я не собираюсь это менять». Например, вполне допустимо иметь const volatileпеременную.
детально
2
Это верно для параметров функции, но для констант в области файла, например, constпеременная действительно доступна только для чтения: constговорит : «Это нельзя изменить». А const volatileс другой стороны говорит: «Это нельзя изменить, но это может измениться». Я добавлю упоминание летучих веществ в мой ответ.
Уильям Моррис
Я не согласен с вашим комментарием, просто с мыслью, что это инструкция для компилятора поместить его в ПЗУ. Это подразумевает некоторую защиту во время выполнения от неопределенного поведения (то есть изменение каким-либо образом через const), что может привести к недоразумениям, таким как «почему я могу изменить эту constпеременную без constуказателя» (распространенный вопрос на SO и любом другом форуме C). !). В целом, мне нравится этот ответ, хотя :)
ловко
Вы правы: «Поместить это в ПЗУ» неверно (хотя и кратко :-) - я поменяю его на «Это нельзя изменить», что более точно. Спасибо за ваши комментарии.
Уильям Моррис
Это отличный обзор. Мне нравится выражаясь , что constобъявляет оба чтения только данные и только для чтения вид из-данных с разницей, «это не может быть изменен» против « вы не можете изменить это».
Джон Перди,
8

Обычно на любом языке программирования рекомендуется использовать constили эквивалентный модификатор, так как

  • Это может прояснить для звонящего, что то, что они передали, не изменится
  • Потенциальные улучшения скорости, так как компилятор наверняка знает, что он может пропустить некоторые вещи, которые имеют значение только в случае изменения параметра
  • Защита от себя, случайно меняющего значение
TheLQ
источник
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ссылку на другое.

lgarcia
источник
0

В соответствии с заявлениями TheLQ:

При работе с командой программистов декларация constявляется хорошим способом указать, что указанная переменная не должна изменяться или просто напоминать о себе в больших проектах. Это полезно в этом смысле и может спасти многие головные боли.

Дэвид Отано
источник