Должен ли я предпочесть константы, а не определения?

В C, я предпочитаю константы по определению? В последнее время я прочитал много кода, и все примеры интенсивно используют определения.

10 ответов

Решение

Нет, в общем, вы не должны использовать объекты с константой в C для создания констант имен. Чтобы создать именованную константу в C, вы должны использовать один из макросов (#define) или перечисления. На самом деле, язык Си не имеет констант, в том смысле, который вы подразумеваете. (C значительно отличается от C++ в этом отношении)

В языке Си понятия константы и константного выражения определены совсем не так, как в С ++. В константе C означает буквальное значение, как 123, Вот несколько примеров констант в C

123
34.58
'x'

Константы в C могут использоваться для построения константных выражений. Однако, поскольку объекты с определенными константами любого типа не являются константами в C, их нельзя использовать в константных выражениях, и, следовательно, вы не можете использовать объекты с константными квалификациями, где требуются постоянные выражения.

Например, следующее не является константой

const int C = 123; /* C is not a constant!!! */

и так как выше C не является константой, ее нельзя использовать для объявления типа массива в области видимости файла

typedef int TArray[C]; /* ERROR: constant expression required */

Не может использоваться в качестве метки кейса

switch (i) {
  case C: ; /* ERROR: constant expression required */
}

Не может использоваться в качестве ширины битового поля

struct S {
  int f : C; /* ERROR: constant expression required */
};

Его нельзя использовать в качестве инициализатора для объекта со статической продолжительностью хранения.

static int i = C; /* ERROR: constant expression required */

Его нельзя использовать как инициализатор перечисления

enum {
  E = C /* ERROR: constant expression required */
};

то есть его нельзя использовать где-либо, где требуется константа.

Это может показаться нелогичным, но именно так определяется язык C.

Вот почему вы видите эти многочисленные #define в коде, с которым вы работаете. Опять же, в языке Си const-квалифицированные объекты имеют очень ограниченное использование. Они практически бесполезны как "константы", поэтому в языке C вы вынуждены использовать #define или перечисляет, чтобы объявить истинные константы.

Конечно, в ситуациях, когда объект, удовлетворяющий константам, работает для вас, то есть выполняет то, что вы хотите, он действительно превосходит макросы во многих отношениях, поскольку он имеет область видимости и типизирован. Вероятно, вам следует отдавать предпочтение таким объектам, где это применимо, однако в общем случае вам придется учитывать вышеизложенные ограничения.

Константы должны быть предпочтительнее, чем defines. Есть несколько преимуществ:

  • Тип безопасности. В то время как C является слабо типизированным языком, используя define теряет все типы безопасности, что позволит компилятору подобрать для вас проблемы.

  • Легкость отладки. Вы можете изменить значение констант через отладчик, в то время как defines автоматически изменяются в коде препроцессором на фактическое значение, а это означает, что если вы хотите изменить значение для целей тестирования / отладки, вам необходимо перекомпилировать.

Возможно, я использовал их неправильно, но, по крайней мере, в gcc, вы не можете использовать константы в операторах case.

const int A=12;
switch (argc) {
    case A:
    break;
}

Хотя этот вопрос специфичен для C, я думаю, это полезно знать:

#include<stdio.h>
int main() {
    const int CON = 123;
    int* A = &CON;
    (*A)++;
    printf("%d\n", CON);  // 124 in C
}

работает в C, но не в C++

Одна из причин использовать #define это избегать таких вещей, чтобы испортить ваш код, особенно это смесь C и C++.

Многие люди здесь дают вам совет в стиле C++. Некоторые даже говорят, что аргументы C++ применимы к C. Это может быть справедливым аргументом. (Является ли это или нет чувствует себя субъективным.) Люди, которые говорят const иногда означает, что что-то другое на двух языках тоже правильно.

Но это, в основном, второстепенные моменты, и лично я думаю, что на самом деле есть относительно незначительные последствия для того или иного пути. Это вопрос стиля, и я думаю, что разные группы людей дадут вам разные ответы.

С точки зрения обычного использования, исторического использования и наиболее распространенного стиля, в C гораздо более типично видеть #define, Использование изомов C++ в C-коде может показаться странным для определенного узкого сегмента C-кодеров. (Включая меня, вот где лежат мои предубеждения.)

Но я удивлен, что никто не предложил промежуточное решение, которое "кажется правильным" на обоих языках: если оно вписывается в группу целочисленных констант, используйте enum,

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

В случаях, подобных приведенным ниже, необходимо использовать определение

  • направляющий переключатель
  • подстановка в исходную строку
  • макросы кода

Пример, где вы должны использовать define over const, это когда у вас номер версии, скажем, 3, и вы хотите, чтобы версия 4 включала некоторые методы, которые недоступны в версии 3

#define VERSION 4
...

#if VERSION==4
   ................
#endif 

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

Константы имеют как тип, так и значение, поэтому они предпочтительнее, когда для вашего значения имеет смысл иметь тип, но не тогда, когда оно не имеет типа (или полиморфно).

Помимо веских причин, приведенных AndreyT для использования DEFINES, а не констант в коде "C", есть еще одна более прагматичная причина для использования DEFINES.

DEFINES легко определить и использовать из (.h) заголовочных файлов, где любой опытный C-кодировщик может ожидать определения определенных констант. Определение констант в заголовочных файлах не так просто - это больше кода, чтобы избежать дублирования определений и т. Д.

Кроме того, аргументы "typesafe" являются спорными, большинство компиляторов будут обнаруживать явные ошибки, такие как задание строки для int и int, или "делать правильные вещи" при небольшом несоответствии, таком как присвоение целого числа плавающей запятой.

Макросы (определяет) могут использоваться препроцессором, а во время компиляции константы не могут.

Вы можете выполнить проверки во время компиляции, чтобы убедиться, что макрос находится в допустимом диапазоне (и #error или #fatal, если это не так). Вы можете использовать значения по умолчанию для макроса, если он еще не был определен. Вы можете использовать макрос размером с массив.

Компилятор может оптимизировать с помощью макросов лучше, чем с константами:

const int SIZE_A = 15;
#define SIZE_B 15

for (i = 0; i < SIZE_A + 1; ++i);    // if not optimized may load A and add 1 on each pass
for (i = 0; i < SIZE_B + 1; ++i);    // compiler will replace "SIZE_B + 1" with 16

Большая часть моей работы связана со встроенными процессорами, в которых нет потрясающих оптимизирующих компиляторов. Возможно, gcc будет обрабатывать SIZE_A как макрос на каком-то уровне оптимизации.

Если это то, что не определено программно, я использую #define, Например, если я хочу, чтобы все мои объекты пользовательского интерфейса имели одинаковое пространство между ними, я мог бы использовать #define kGUISpace 20,

Другие вопросы по тегам