"static const" против "#define" против "enum"
Какой из них лучше использовать среди приведенных ниже утверждений в C?
static const int var = 5;
или же
#define var 5
или же
enum { var = 5 };
17 ответов
Вообще говоря:
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_), при просмотре библиотеки вы заметите, что за ней (в общем случае) следует название конкретной области (библиотеки), затем с осмысленным именем.
Это обычно делает для длинных имен:)
Это зависит от того, для чего вам нужно значение. Вы (и все остальные до сих пор) пропустили третий вариант:
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) не позволяют этого.
Таким образом, в большинстве случаев предпочитайте enum альтернативам. В противном случае, первый и последний пункты пули, вероятно, будут контролирующими факторами, и вам нужно подумать над тем, чтобы удовлетворить оба сразу.
Если бы вы спрашивали о C++, то каждый раз использовали бы option (1) - static const.
В C конкретно? В C правильный ответ: использовать #define
(или, если необходимо, enum
)
Хотя полезно иметь обзорные и типизирующие свойства const
объект, на самом деле const
объекты в C (в отличие от C++) не являются истинными константами и поэтому обычно бесполезны в большинстве практических случаев.
Таким образом, в C выбор должен определяться тем, как вы планируете использовать свою константу. Например, вы не можете использовать const int
объект как case
метка (пока макрос будет работать). Вы не можете использовать const int
объект как ширина битового поля (пока макрос будет работать). В C89/90 вы не можете использовать const
объект для указания размера массива (пока макрос будет работать). Даже в C99 вы не можете использовать const
объект, чтобы указать размер массива, когда вам нужен массив не VLA.
Если это важно для вас, это будет определять ваш выбор. Большую часть времени у вас не будет выбора, кроме как использовать #define
в C. И не забудьте другую альтернативу, которая производит истинные константы в C - enum
,
В C++ const
объекты являются истинными константами, поэтому в C++ почти всегда лучше отдавать предпочтение const
вариант (нет необходимости в явном static
в C++, хотя).
Разница между static const
а также #define
является то, что первый использует память, а второй не использует память для хранения. Во-вторых, вы не можете передать адрес #define
в то время как вы можете передать адрес static const
, На самом деле, в зависимости от того, в каких обстоятельствах мы находимся, нам нужно выбрать одно из этих двух. Оба в своих лучших проявлениях при разных обстоятельствах. Пожалуйста, не думайте, что одно лучше другого...:-)
Если бы это было так, Деннис Ричи оставил бы лучшего в покое... хахаха...:-)
В С #define
гораздо популярнее Вы можете использовать эти значения для объявления размеров массива, например:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
ANSI C не позволяет вам использовать static const
Насколько я знаю, в этом контексте. В C++ вам следует избегать макросов в этих случаях. Ты можешь написать
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
и даже пропустить static
потому что внутренняя связь подразумевается const
уже [только в C++].
Еще один недостаток 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;
Даже это не работает с const, так как компилятор не видит его как константу:
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
в этих случаях иначе...
Если вы можете сойти с рук, static const
имеет много преимуществ. Он подчиняется обычным принципам области видимости, отображается в отладчике и в целом подчиняется правилам, которым подчиняются переменные.
Тем не менее, по крайней мере, в оригинальном стандарте C он не является константой. Если вы используете #define var 5
, ты можешь написать int foo[var];
как объявление, но вы не можете сделать это (кроме как расширение компилятора "с static const int var = 5;
, Это не так в C++, где static const
версия может быть использована везде #define
Версия может, и я считаю, что это также имеет место с C99.
Тем не менее, никогда не называйте #define
константа с именем в нижнем регистре. Он будет отменять любое возможное использование этого имени до конца модуля перевода. Макросконстанты должны находиться в том, что фактически является их собственным пространством имен, которое традиционно состоит из заглавных букв, возможно, с префиксом.
ВСЕГДА предпочтительнее использовать 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...
#define var 5
доставит вам неприятности, если у вас есть такие вещи, как mystruct.var
,
Например,
struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
}
Препроцессор заменит его, и код не скомпилируется. По этой причине традиционный стиль кодирования предполагает постоянную #define
s использует заглавные буквы, чтобы избежать конфликта.
Я написал программу быстрого тестирования, чтобы продемонстрировать одно отличие:
#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
^
Обратите внимание, что enum выдает ошибку, когда define дает предупреждение.
Хотя вопрос был о целых числах, стоит отметить, что #define и перечисления бесполезны, если вам нужна постоянная структура или строка. Они оба обычно передаются в функции как указатели. (Со строками это требуется; со структурами это намного более эффективно.)
Что касается целых чисел, если вы находитесь во встроенной среде с очень ограниченным объемом памяти, вам может потребоваться беспокоиться о том, где хранится константа и как компилируются обращения к ней. Компилятор может добавить две константы во время выполнения, но добавить две #defines во время компиляции. Константа #define может быть преобразована в одну или несколько MOV [немедленных] инструкций, что означает, что константа эффективно сохраняется в памяти программы. Константа const будет храниться в разделе.const в памяти данных. В системах с гарвардской архитектурой могут быть различия в производительности и использовании памяти, хотя они, вероятно, будут небольшими. Они могут иметь значение для оптимизации внутренних циклов.
Определение
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
это единственный способ определить постоянное значение, которое нельзя изменить никакими средствами.
Кстати, альтернатива #define
, который обеспечивает правильную область видимости, но ведет себя как "настоящая" константа, это "enum". Например:
enum {number_ten = 10;}
Во многих случаях полезно определять перечисляемые типы и создавать переменные этих типов; если это сделано, отладчики могут отображать переменные в соответствии с именем перечисления.
Однако при этом необходимо сделать одно важное замечание: в C++ перечисляемые типы имеют ограниченную совместимость с целыми числами. Например, по умолчанию нельзя выполнять арифметику с ними. Я нахожу это любопытным поведением по умолчанию для перечислений; хотя было бы неплохо иметь тип "строгого перечисления", учитывая желание иметь C++, в целом совместимый с C, я думаю, что поведение по умолчанию для типа "enum" должно быть взаимозаменяемым с целыми числами.
Не думаю, что есть ответ на вопрос "что всегда лучше", но, как сказал Матье
static const
Тип безопасен. Моя самая большая любимая мозоль с #define
тем не менее, при отладке в Visual Studio вы не можете наблюдать за переменной. Это дает ошибку, что символ не может быть найден.
Простая разница:
Во время предварительной обработки константа заменяется ее значением. Поэтому вы не можете применить оператор разыменования к определению, но вы можете применить оператор разыменования к переменной.
Как и следовало ожидать, определить быстрее, чем статическое const.
Например, имея:
#define mymax 100
ты не можешь сделать printf("address of constant is %p",&mymax);
,
Но имея
const int mymax_var=100
ты можешь сделать printf("address of constant is %p",&mymax_var);
,
Чтобы быть более понятным, определение заменяется его значением на этапе предварительной обработки, поэтому у нас нет переменной, хранящейся в программе. У нас есть только код из текстового сегмента программы, где использовалось определение.
Однако для статического const у нас есть переменная, которая где-то размещена. Для gcc статические константы размещаются в текстовом сегменте программы.
Выше я хотел рассказать об операторе ссылки, поэтому замените разыменование ссылкой.
Мы рассмотрели полученный код ассемблера на MBF16X... Оба варианта приводят к одному и тому же коду для арифметических операций (например, ADD Immediate).
Так const int
предпочтительнее для проверки типа, в то время как #define
это старый стиль. Может быть, это зависит от компилятора. Так что проверьте ваш созданный код ассемблера.
Я не уверен, прав ли я, но по моему мнению звоню #define
Значение d намного быстрее, чем вызов любой другой обычно объявленной переменной (или константного значения). Это потому, что когда программа работает и ей нужно использовать некоторую обычно объявленную переменную, ей нужно перейти в точное место в памяти, чтобы получить эту переменную.
В противоположность когда это используют #define
Значение d, программе не нужно переходить к какой-либо выделенной памяти, она просто принимает значение. Если #define myValue 7
и программа вызова myValue
, он ведет себя точно так же, как когда он просто вызывает 7
,