Intereting Posts
Конвертируйте одно 32-битное число с плавающей запятой в два 16-разрядных uint-номера, а затем снова конвертируйте их обратно в 32-битный Простая программа для драйвера устройства Linux В чем разница в том, как C и Java обрабатывают доступ к границам массивов? Создание stdin без блокировки Расширить динамическую связанную общую библиотеку? Поддерживает ли API-интерфейс Couchbase REST данные NON-JSON (двоичные данные) Что такое использование # на C или C ++? Венгерская нотация FreeRTOS C Связанный список valgrind Неверный просмотр размера Побитовая передача из 8 байтов Почему значение локальной переменной все еще существует, когда управление выходит из функции Смещения памяти в встроенной сборке Почему `free` в C не берет количество байтов, которые нужно освободить? CRC32 алгоритм / реализация в C без таблицы поиска и с открытой лицензией Битовый сдвиг байта более чем на 8 бит

“Static const” vs “#define” vs “enum”

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

Это обычно делает для длинных имен 🙂

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

  1. static const int var = 5;
  2. #define var 5
  3. 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) не допускают этого.

Таким образом, в большинстве контекстов предпочитайте «перечисление» по альтернативам. В противном случае, первые и последние точки пули, скорее всего, будут контролирующими факторами – и вам нужно думать сложнее, если вам нужно удовлетворить оба одновременно.

Если вы спрашивали о 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 .