"static const" vs "#define" vs "enum"

Какой из них лучше использовать среди приведенных ниже инструкций в C?

static const int var = 5;

или

#define var 5

или

enum { var = 5 };

Ответ 1

Вообще говоря:

static const

Поскольку он уважает область действия и безопасен по типу.

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

#ifdef VAR // Very bad name, not long enough, too general, etc..
  static int const var = VAR;
#else
  static int const var = 5; // default value
#endif

Если возможно, вместо макроса/эллипса используйте альтернативу типа.

Если вам действительно нужно пойти с макросом (например, вы хотите __FILE__ или __LINE__), то лучше назовите свой макрос ОЧЕНЬ тщательно: в соглашение об именах Boost рекомендует весь верхний регистр, начиная с названия проекта (здесь BOOST_), просматривая библиотеку, вы заметите, что это (обычно), за которым следует имя конкретной области (библиотеки), затем с содержательным именем.

Обычно это относится к длительным именам:)

Ответ 2

Это зависит от того, для чего вам нужно значение. Вы (и все остальные до сих пор) опустили третий вариант:

  • static const int var = 5;
  • #define var 5
  • enum { var = 5 };

Игнорируя проблемы выбора имени, затем:

  • Если вам нужно передать указатель, вы должны использовать (1).
  • Так как (2), по-видимому, является опцией, вам не нужно передавать указатели.
  • Оба (1) и (3) имеют символ в таблице символов отладчика, что упрощает отладку. Скорее всего, что (2) не будет иметь символа, оставляя вас интересно, что это такое.
  • (1) не может использоваться как измерение для массивов в глобальном масштабе; оба (2) и (3) могут.
  • (1) не может использоваться как измерение для статических массивов в области функций; оба (2) и (3) могут.
  • В C99 все они могут использоваться для локальных массивов. Технически, использование (1) подразумевало бы использование VLA (массив переменной длины), хотя размер, на который ссылается "var", конечно, был бы зафиксирован в размере 5.
  • (1) не может использоваться в таких местах, как команды switch; оба (2) и (3) могут.
  • (1) не может использоваться для инициализации статических переменных; оба (2) и (3) могут.
  • (2) может изменить код, который вы не хотите изменять, потому что он используется препроцессором; оба (1) и (3) не будут иметь неожиданных побочных эффектов.
  • Вы можете определить, было ли (2) установлено в препроцессоре; ни (1), ни (3) не допускают этого.

Итак, в большинстве контекстов предпочитайте "перечисление" по альтернативам. В противном случае, первая и последняя точки пули, скорее всего, будут контролирующими факторами, и вам нужно подумать о трудностях, если вам нужно удовлетворить оба момента.

Если вы спрашивали о С++, тогда вы будете использовать параметр (1) - статический const - каждый раз.

Ответ 3

В С, в частности? В C правильный ответ: используйте #define (или, при необходимости, enum)

В то время как полезно иметь свойства определения области охвата и типирования объекта const, в действительности const объекты в C (в отличие от С++) не являются истинными константами и поэтому обычно являются бесполезными в большинстве практических случаев.

Итак, в C выбор должен определяться тем, как вы планируете использовать свою константу. Например, вы не можете использовать объект const int в качестве метки case (в то время как макрос будет работать). Вы не можете использовать объект const int в качестве ширины битового поля (в то время как макрос будет работать). В C89/90 вы не можете использовать объект const для указания размера массива (в то время как макрос будет работать). Даже в C99 вы не можете использовать объект const для указания размера массива, когда вам нужен массив

Ответ 4

Разница между static const и #define заключается в том, что первая использует память, а позже не использует память для хранения. Во-вторых, вы не можете передать адрес #define, тогда как вы можете передать адрес static const. На самом деле это зависит от того, какое обстоятельство мы находимся, нам нужно выбрать один из этих двух. Оба они в своих лучших проявлениях при разных обстоятельствах. Пожалуйста, не предполагайте, что один лучше другого...: -)

Если бы это было так, Деннис Ричи сохранил бы лучший один один... hahaha...: -)

Ответ 5

В C #define гораздо более популярна. Вы можете использовать эти значения для объявления размеров массива, например:

#define MAXLEN 5

void foo(void) {
   int bar[MAXLEN];
}

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

const int maxlen = 5;

void foo() {
   int bar[maxlen];
}

и даже оставить вне static, потому что внутренняя привязка подразумевается const уже [только в С++].

Ответ 6

Другим недостатком const в C является то, что вы не можете использовать значение при инициализации другого const.

static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;

// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND 
                                     * NUMBER_OF_HANDS;

Даже это не работает с константой, поскольку компилятор не видит ее как константу:

static uint8_t const ARRAY_SIZE = 16;
static int8_t const lookup_table[ARRAY_SIZE] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant!

Я был бы счастлив использовать типизированные const в этих случаях, в противном случае...

Ответ 7

Если вам это удастся, static const имеет много преимуществ. Он подчиняется нормальным принципам видимости, видимым в отладчике и обычно подчиняется правилам, которым подчиняются переменные.

Однако, по крайней мере, в исходном стандарте C он фактически не является константой. Если вы используете #define var 5, вы можете написать int foo[var]; как объявление, но вы не можете этого сделать (кроме как расширение компилятора "с static const int var = 5;. Это не так в С++, где версия static const может использоваться везде, где может быть версия #define, и я считаю, что это также относится к C99.

Однако никогда не называйте константу #define с нижним регистром. Он будет отменять любое возможное использование этого имени до конца единицы перевода. Макро-константы должны быть в том, что фактически является их собственным пространством имен, которое традиционно является прописными буквами, возможно, с префиксом.

Ответ 8

Я написал программу быстрого тестирования, чтобы продемонстрировать одну разницу:

#include <stdio.h>

enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};

#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32

int main(int argc, char *argv[]) {

   printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);

   return(0);
}

Скомпилируется с этими ошибками и предупреждениями:

main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
      ^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
      ^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
        ^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
        ^

Обратите внимание, что перечисление дает ошибку, когда define дает предупреждение.

Ответ 9

#define var 5 вызовет проблемы, если у вас есть такие вещи, как mystruct.var.

Например,

struct mystruct {
    int var;
};

#define var 5

int main() {
    struct mystruct foo;
    foo.var = 1;
    return 0;
}

Препроцессор заменит его, и код не будет компилироваться. По этой причине традиционный стиль кодирования предполагает, что все константы #define используют заглавные буквы, чтобы избежать конфликтов.

Ответ 10

ВСЕГДА предпочтительнее использовать const, а не #define. Это потому, что const обрабатывается компилятором и #define препроцессором. Это как #define сам не является частью кода (грубо говоря).

Пример:

#define PI 3.1416

Символьное имя PI никогда не будет видно компиляторам; он может быть удален препроцессором до того, как исходный код даже попадет в компилятор. В результате имя PI не может быть введено в таблицу символов. Это может сбивать с толку, если вы получаете ошибку во время компиляции с использованием константы, потому что сообщение об ошибке может ссылаться на 3.1416, а не на PI. Если PI были определены в заголовочном файле, который вы не писали, вы не знаете, откуда этот 3.1416.

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

Решение:

const double PI = 3.1416; //or static const...

Ответ 11

Определение

const int const_value = 5;

не всегда определяет постоянное значение. Некоторые компиляторы (например tcc 0.9.26) просто выделяют память, идентифицированную с именем "const_value". Используя идентификатор "const_value", вы не можете изменить эту память. Но вы все еще можете изменить память с помощью другого идентификатора:

const int const_value = 5;
int *mutable_value = (int*) &const_value;
*mutable_value = 3;
printf("%i", const_value); // The output may be 5 or 3, depending on the compiler.

Это означает, что определение

#define CONST_VALUE 5

- единственный способ определить постоянное значение, которое никак не может быть изменено.

Ответ 12

Не думайте, что есть ответ на "что всегда лучше", но, как сказал Маттие

static const

безопасен по типу. Мое самое большое домашнее животное с #define, однако, заключается в отладке Visual Studio, вы не можете смотреть эту переменную. Это дает ошибку, что символ не может быть найден.

Ответ 13

Кстати, альтернатива #define, которая обеспечивает правильное определение области, но ведет себя как "реальная" константа, является "перечислением". Например:

enum {number_ten = 10;}

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

Одно из важных предостережений с этим, однако: в С++ перечисленные типы имеют ограниченную совместимость с целыми числами. Например, по умолчанию невозможно выполнить арифметику. Я считаю, что это любопытное поведение по умолчанию для перечислений; в то время как было бы неплохо иметь тип "строгого перечисления", учитывая желание иметь С++ в целом совместимый с C, я думаю, что поведение по умолчанию типа "enum" должно быть взаимозаменяемым с целыми числами.

Ответ 14

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

Что касается целых чисел, если вы находитесь во встроенной среде с очень ограниченной памятью, вам может потребоваться рассказать о том, где хранится константа и как скомпилированы ее обращения к ней. Компилятор может добавить две константы во время выполнения, но добавьте два #defines во время компиляции. Константа #define может быть преобразована в одну или несколько команд MOV [немедленного], что означает, что константа эффективно сохраняется в памяти программы. Константная константа будет храниться в секции .const в памяти данных. В системах с архитектурой Гарварда могут быть различия в производительности и использовании памяти, хотя они, вероятно, будут небольшими. Они могут иметь значение для жесткой оптимизации внутренних циклов.

Ответ 15

Простая разница:

При времени предварительной обработки константа заменяется на ее значение. Таким образом, вы не можете применить оператор разыменования к определению, но вы можете применить оператор разыменования к переменной.

Как вы могли предположить, быстрее определить статический const.

Например, имея:

#define mymax 100

вы не можете сделать printf("address of constant is %p",&mymax);.

Но имея

const int mymax_var=100

вы можете сделать printf("address of constant is %p",&mymax_var);.

Чтобы быть более понятным, определение заменяется его значением на этапе предварительной обработки, поэтому у нас нет какой-либо переменной, хранящейся в программе. У нас есть только код из текстового сегмента программы, в котором используется определение.

Однако для static const у нас есть переменная, которая где-то выделена. Для gcc статические константы выделяются в текстовом сегменте программы.

Выше я хотел рассказать о ссылочном операторе, поэтому замените разыменовывание ссылкой.

Ответ 16

Мы рассмотрели созданный код ассемблера на MBF16X... Оба варианта приводят к одному и тому же коду для арифметических операций (например, ADD Immediate).

Так что const int является предпочтительным для проверки типа, тогда как #define является старым. Возможно, он специфичен для компилятора. Поэтому проверьте полученный код ассемблера.

Ответ 17

Я не уверен, прав ли я, но, по моему мнению, вызов значения #define d намного быстрее, чем вызов любой другой обычно объявленной переменной (или константного значения). Это потому, что когда программа работает и ей нужно использовать некоторую обычно объявленную переменную, ей нужно перейти в точное место в памяти, чтобы получить эту переменную.

Напротив, когда используется значение #define d, программе не нужно переходить к какой-либо выделенной памяти, она просто принимает значение. Если #define myValue 7 и программа, вызывающая myValue, она ведет себя точно так же, как когда она просто вызывает 7.

Ответ 18

Я видел метод #define, используемый для управления областью константы, таким образом имитируя переменную private class в С++.

#define MAXL          4
// some code
#undef MAXL