Какой из них лучше использовать среди приведенных ниже инструкций в 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 рекомендует весь верхний регистр, начиная с названия проекта (здесь BOOST_), просматривая библиотеку, вы заметите, что это (обычно), за которым следует имя конкретной области (библиотеки), затем с содержательным именем.
Обычно это относится к длительным именам:)
Это зависит от того, для чего вам нужно значение. Вы (и все остальные до сих пор) опустили третий вариант:
static const int var = 5;
#define var 5
enum { var = 5 };
Игнорируя проблемы выбора имени, затем:
Итак, в большинстве контекстов предпочитайте "перечисление" по альтернативам. В противном случае, первая и последняя точки пули, скорее всего, будут контролирующими факторами, и вам нужно подумать о трудностях, если вам нужно удовлетворить оба момента.
Если вы спрашивали о С++, тогда вы будете использовать параметр (1) - статический const - каждый раз.
enum
является то, что они реализованы как int
([C99] 6.7.2.2/3). #define
позволяет указывать unsigned и long с суффиксами U
и L
, а const
позволяет указывать тип. enum
может вызвать проблемы с обычными преобразованиями типов.
В С, в частности? В C правильный ответ: используйте #define
(или, при необходимости, enum
)
В то время как полезно иметь свойства определения области охвата и типирования объекта const
, в действительности const
объекты в C (в отличие от С++) не являются истинными константами и поэтому обычно являются бесполезными в большинстве практических случаев.
Итак, в C выбор должен определяться тем, как вы планируете использовать свою константу. Например, вы не можете использовать объект const int
в качестве метки case
(в то время как макрос будет работать). Вы не можете использовать объект const int
в качестве ширины битового поля (в то время как макрос будет работать). В C89/90 вы не можете использовать объект const
для указания размера массива (в то время как макрос будет работать). Даже в C99 вы не можете использовать объект const
для указания размера массива, когда вам нужен массив
const int
в case-метках запрещено во всех версиях языка Си. (Разумеется, ваш компилятор может поддерживать его как нестандартное расширение языка C ++.)
Разница между static const
и #define
заключается в том, что первая использует память, а позже не использует память для хранения. Во-вторых, вы не можете передать адрес #define
, тогда как вы можете передать адрес static const
. На самом деле это зависит от того, какое обстоятельство мы находимся, нам нужно выбрать один из этих двух. Оба они в своих лучших проявлениях при разных обстоятельствах. Пожалуйста, не предполагайте, что один лучше другого...: -)
Если бы это было так, Деннис Ричи сохранил бы лучший один один... hahaha...: -)
В 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
уже [только в С++].
const int MY_CONSTANT = 5;
в один файл и получить к нему доступ extern const int MY_CONSTANT;
в другой. Я не мог найти любую информацию в стандарте (C99 , по крайней мере) о const
изменения поведения по умолчанию «6.2.2: 5 Если объявление в идентифицируемом эр для объекта имеет фи ль сфера применения и не хранение класса специфического эр, его связь является внешним» ,
Другим недостатком 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 uint8_t const ARRAY_SIZE = 16;
внезапно перестал компилироваться может быть немного сложно, особенно когда #define ARRAY_SIZE 256
десять уровней в запутанной паутине заголовков. Что все заглавные буквы ARRAY_SIZE
на неприятности. Зарезервируйте ALL_CAPS для макросов и никогда не определяйте макрос, который не находится в форме ALL_CAPS.
Если вам это удастся, static const
имеет много преимуществ. Он подчиняется нормальным принципам видимости, видимым в отладчике и обычно подчиняется правилам, которым подчиняются переменные.
Однако, по крайней мере, в исходном стандарте C он фактически не является константой. Если вы используете #define var 5
, вы можете написать int foo[var];
как объявление, но вы не можете этого сделать (кроме как расширение компилятора "с static const int var = 5;
. Это не так в С++, где версия static const
может использоваться везде, где может быть версия #define
, и я считаю, что это также относится к C99.
Однако никогда не называйте константу #define
с нижним регистром. Он будет отменять любое возможное использование этого имени до конца единицы перевода. Макро-константы должны быть в том, что фактически является их собственным пространством имен, которое традиционно является прописными буквами, возможно, с префиксом.
const
в C99 все еще не является настоящей константой. Вы можете объявить размер массива с помощью const
в C99, но только потому, что C99 поддерживает массивы переменной длины. По этой причине он будет работать только там, где разрешены VLA. Например, даже в C99 вы все еще не можете использовать const
для объявления размера массива-члена в struct
.
const int
как если бы это был C ++ const или макрос. Если вы хотите зависеть от этого отклонения GCC от стандарта, это, конечно, ваш выбор, я бы лично согласился с этим, если вы не можете на самом деле предвидеть использование другого компилятора, кроме GCC или Clang, последний обладает той же функцией здесь (протестировано с Clang). 3.7).
Я написал программу быстрого тестирования, чтобы продемонстрировать одну разницу:
#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 дает предупреждение.
ВСЕГДА предпочтительнее использовать 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
используют заглавные буквы, чтобы избежать конфликтов.
Определение
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
также можно изменить, отредактировав машинный код.
5
. Но нельзя изменить #define
потому что это макрос препроцессора. Это не существует в двоичной программе. Если кто-то хотел изменить все места, где использовался CONST_VALUE
, он должен был сделать это одно за другим.
Кстати, альтернатива #define
, которая обеспечивает правильное определение области, но ведет себя как "реальная" константа, является "перечислением". Например:
enum {number_ten = 10;}
Во многих случаях полезно определять перечисленные типы и создавать переменные этих типов; если это будет сделано, отладчики могут отображать переменные в соответствии с их именем перечисления.
Одно из важных предостережений с этим, однако: в С++ перечисленные типы имеют ограниченную совместимость с целыми числами. Например, по умолчанию невозможно выполнить арифметику. Я считаю, что это любопытное поведение по умолчанию для перечислений; в то время как было бы неплохо иметь тип "строгого перечисления", учитывая желание иметь С++ в целом совместимый с C, я думаю, что поведение по умолчанию типа "enum" должно быть взаимозаменяемым с целыми числами.
int
, поэтому «enum hack» не может использоваться с другими целочисленными типами. (Тип перечисления совместит с некоторым определяемой реализацией целого типа, не обязательно int
, но в данном случае типа является анонимным , так что не имеет значения.)
int
переменной с типом перечисления (какие компиляторы могут это делать), и каждый пытается присвоить такой переменная член собственного перечисления. Хотелось бы, чтобы комитеты по стандартам добавили переносимые способы объявления целочисленных типов с заданной семантикой. ЛЮБАЯ платформа, независимо от размера char
, должна иметь возможность, например, объявлять тип, который обернет мод 65536, даже если компилятор должен добавить множество AND R0,#0xFFFF
или эквивалентных инструкций.
Не думайте, что есть ответ на "что всегда лучше", но, как сказал Маттие
static const
безопасен по типу. Мое самое большое домашнее животное с #define
, однако, заключается в отладке Visual Studio, вы не можете смотреть эту переменную. Это дает ошибку, что символ не может быть найден.
Хотя вопрос касался целых чисел, стоит отметить, что #define и перечисления бесполезны, если вам нужна постоянная структура или строка. Они оба обычно передаются в функции в качестве указателей. (С строками, которые требуются, с структурами он намного эффективнее.)
Что касается целых чисел, если вы находитесь во встроенной среде с очень ограниченной памятью, вам может потребоваться рассказать о том, где хранится константа и как скомпилированы ее обращения к ней. Компилятор может добавить две константы во время выполнения, но добавьте два #defines во время компиляции. Константа #define может быть преобразована в одну или несколько команд MOV [немедленного], что означает, что константа эффективно сохраняется в памяти программы. Константная константа будет храниться в секции .const в памяти данных. В системах с архитектурой Гарварда могут быть различия в производительности и использовании памяти, хотя они, вероятно, будут небольшими. Они могут иметь значение для жесткой оптимизации внутренних циклов.
Простая разница:
При времени предварительной обработки константа заменяется на ее значение. Таким образом, вы не можете применить оператор разыменования к определению, но вы можете применить оператор разыменования к переменной.
Как вы могли предположить, быстрее определить статический 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 статические константы выделяются в текстовом сегменте программы.
Выше я хотел рассказать о ссылочном операторе, поэтому замените разыменовывание ссылкой.
const
. C не имеет символьных констант, кроме перечислимых . const int
- это переменная. Вы также путаете язык и конкретные реализации. Там нет требования, где разместить объект. И это не верно даже для GCC: как правило , он помещает const
квалифицированные переменные в .rodata
раздела. Но это зависит от целевой платформы. И вы имеете в виду адрес оператора &
.
Я не уверен, прав ли я, но, по моему мнению, вызов значения #define
d намного быстрее, чем вызов любой другой обычно объявленной переменной (или константного значения). Это потому, что когда программа работает и ей нужно использовать некоторую обычно объявленную переменную, ей нужно перейти в точное место в памяти, чтобы получить эту переменную.
Напротив, когда используется значение #define
d, программе не нужно переходить к какой-либо выделенной памяти, она просто принимает значение. Если #define myValue 7
и программа, вызывающая myValue
, она ведет себя точно так же, как когда она просто вызывает 7
.
Я видел метод #define, используемый для управления областью константы, таким образом имитируя переменную private class в С++.
#define MAXL 4
// some code
#undef MAXL
Мы рассмотрели созданный код ассемблера на MBF16X... Оба варианта приводят к одному и тому же коду для арифметических операций (например, ADD Immediate).
Так что const int
является предпочтительным для проверки типа, тогда как #define
является старым. Возможно, он специфичен для компилятора. Поэтому проверьте полученный код ассемблера.