Должен ли я предпочесть константы определению? - PullRequest
45 голосов
/ 22 февраля 2010

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

Ответы [ 10 ]

100 голосов
/ 22 февраля 2010

Нет, в общем, вы не должны использовать объекты с константой в C для создания констант имен. Для создания именованной константы в C вы должны использовать либо макросы (#define), либо перечисления. На самом деле, язык Си не имеет констант, в том смысле, который вы подразумеваете. (C значительно отличается от 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 или перечисления для объявления истинных констант.

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

10 голосов
/ 22 февраля 2010

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

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

  • Простота отладки . Вы можете изменить значение констант через отладчик, в то время как препроцессор автоматически поменяет define s в коде на значение фактическое , то есть, если измените значение для целей тестирования / отладки, вам нужно перекомпилировать.

7 голосов
/ 22 февраля 2010

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

const int A=12;
switch (argc) {
    case A:
    break;
}
6 голосов
/ 12 мая 2010

Хотя этот вопрос относится к 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 ++.

5 голосов
/ 22 февраля 2010

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

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

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

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

3 голосов
/ 22 февраля 2010

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

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

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

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

#define VERSION 4
...

#if VERSION==4
   ................
#endif 
2 голосов
/ 22 февраля 2010

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

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

1 голос
/ 22 февраля 2010

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

Вы можете выполнять проверки во время компиляции, чтобы убедиться, что макрос находится в допустимом диапазоне (и #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 как макрос на каком-то уровне оптимизации.

1 голос
/ 22 февраля 2010

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

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

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

1 голос
/ 22 февраля 2010

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

...