Какой из них лучше использовать среди приведенных ниже инструкций в C?
static const int var = 5;
или же
#define var 5
или же
enum { var = 5 };
Вообще говоря:
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 рекомендует все верхние __LINE__
, начиная с названия проекта ( здесь BOOST_), просматривая библиотеку, вы заметите, что это (обычно), за которым следует имя конкретной области (библиотеки), а затем с содержательным именем.
Это обычно делает для длинных имен 🙂
Это зависит от того, для чего вам нужно значение. Вы (и все остальные до сих пор) опустили третий вариант:
static const int var = 5;
#define var 5
enum { var = 5 };
Игнорируя проблемы выбора имени, тогда:
Таким образом, в большинстве контекстов предпочитайте «перечисление» по альтернативам. В противном случае, первые и последние точки пули, скорее всего, будут контролирующими факторами – и вам нужно думать сложнее, если вам нужно удовлетворить оба одновременно.
Если вы спрашивали о C ++, тогда вы будете использовать параметр (1) – статический const – каждый раз.
В С, в частности? В 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
. На самом деле это зависит от того, какое обстоятельство мы находимся, нам нужно выбрать один из этих двух. Оба они в своих лучших проявлениях при разных обстоятельствах. Пожалуйста, не думайте, что один лучше другого … 🙂
Если бы это было так, Деннис Ритчи сохранил бы лучший один один … хахаха … 🙂
В C #define
гораздо более популярна. Вы можете использовать эти значения для объявления размеров массива, например:
#define MAXLEN 5 void foo(void) { int bar[MAXLEN]; }
Насколько я знаю, ANSI C не позволяет использовать static const
s в этом контексте. В 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;
Даже это не работает с константой, поскольку компилятор не видит ее как константу:
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
версия static const
может использоваться везде, где может быть #define
версия, и я считаю, что это также относится к C99.
Однако никогда не #define
константу #define
с нижним регистром. Он будет отменять любое возможное использование этого имени до конца единицы перевода. Макро-константы должны быть в том, что фактически является их собственным пространством имен, которое традиционно является прописными буквами, возможно, с префиксом.
Я написал программу быстрого тестирования, чтобы продемонстрировать одну разницу:
#include 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 дает предупреждение.
#define var 5
вызовет у вас проблемы, если у вас есть такие вещи, как mystruct.var
.
Например,
struct mystruct { int var; }; #define var 5 int main() { struct mystruct foo; foo.var = 1; return 0; }
Препроцессор заменит его, и код не будет компилироваться. По этой причине традиционный стиль кодирования предполагает, что все константные #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...
Определение
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
это единственный способ определить постоянное значение, которое никак не может быть изменено.
Не думайте, что есть ответ на вопрос «что всегда лучше», но, как сказал Маттиу
static const
безопасен. Мое самое большое домашнее животное peeve с #define
, однако, это когда отладка в Visual Studio вы не можете наблюдать за переменной. Это дает ошибку, что символ не может быть найден.
Кстати, альтернативой #define
, которая обеспечивает правильное определение области, но ведет себя как «реальная» константа, является «enum». Например:
enum {number_ten = 10;}
Во многих случаях полезно определять перечисленные типы и создавать переменные этих типов; если это будет сделано, отладчики могут отображать переменные в соответствии с их именем enums.
Одно из важных предостережений при этом, однако: в C ++ перечисленные типы имеют ограниченную совместимость с целыми числами. Например, по умолчанию невозможно выполнить арифметику. Я считаю, что это любопытное поведение по умолчанию для перечислений; в то время как было бы неплохо иметь тип «строгого enums», учитывая желание иметь C ++, в целом совместимый с C, я думаю, что поведение по умолчанию типа «enum» должно быть взаимозаменяемым с целыми числами.
Хотя вопрос касался целых чисел, стоит отметить, что #define и enums бесполезны, если вам нужна постоянная структура или строка. Они оба обычно передаются в функции в качестве указателей. (С строками это необходимо, со структурами это намного эффективнее.)
Что касается целых чисел, если вы во встроенной среде с очень ограниченной памятью, вам может понадобиться беспокоиться о том, где хранится константа и как скомпилированы ее обращения к ней. Компилятор может добавить две константы во время выполнения, но добавьте два #defines во время компиляции. Константа #define может быть преобразована в одну или несколько команд MOV [немедленного], что означает, что константа эффективно сохраняется в памяти программы. Константная константа будет храниться в секции .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 статические константы выделяются в текстовом сегменте программы.
Выше я хотел рассказать о ссылочном операторе, поэтому заменим разыменование ссылкой.
Мы рассмотрели полученный код ассемблера на MBF16X … Оба варианта приводят к одному и тому же коду для арифметических операций (например, ADD Immediate).
Так что const int
является предпочтительным для проверки типа, а #define
– старым. Возможно, он специфичен для компилятора. Поэтому проверьте полученный код ассемблера.
Я не уверен, что я прав, но, на мой взгляд, вызов #define
d value намного быстрее, чем вызов любой другой обычно объявленной переменной (или значения const). Это связано с тем, что, когда программа запущена, и ей нужно использовать какую-то обычно объявленную переменную, ей необходимо перейти в точное место в памяти, чтобы получить эту переменную.
В противоположном случае, когда используется значение #define
d, программе не нужно переходить на любую выделенную память, она просто принимает значение. Если #define myValue 7
и программа, вызывающая myValue
, она ведет себя точно так же, как при вызове 7
.