Какой из них лучше использовать среди приведенных ниже утверждений в C?
static const int var = 5;
или
#define var 5
или
enum { var = 5 };
Какой из них лучше использовать среди приведенных ниже утверждений в C?
static const int var = 5;
или
#define var 5
или
enum { var = 5 };
Ответы:
Это зависит от того, для чего вам нужно значение. Вы (и все остальные до сих пор) пропустили третий вариант:
static const int var = 5;
#define var 5
enum { var = 5 };
Игнорируя вопросы о выборе имени, тогда:
Таким образом, в большинстве случаев предпочитайте enum альтернативам. В противном случае, первый и последний пункты пули, вероятно, будут контролирующими факторами, и вам нужно подумать над тем, чтобы удовлетворить оба сразу.
Если бы вы спрашивали о C ++, то каждый раз использовали бы option (1) - static const.
enum
является то, что они реализованы как int
([C99] 6.7.2.2/3). A #define
позволяет вам указать unsigned и long с U
и L
суффиксами, а также const
дать вам тип. enum
может вызвать проблемы с обычными преобразованиями типов.
enum
не #define
использует дополнительное пространство, как таковое. Значение будет отображаться в объектном коде как часть инструкций, а не как выделенное хранилище в сегменте данных, в куче или в стеке. У вас будет немного места для static const int
, но компилятор может оптимизировать его, если вы не берете адрес.
enum
s (и static const
): их нельзя изменить. a define
может быть #undefine
'd, где an enum
и static const
фиксированы к данному значению.
Вообще говоря:
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
должны оставаться только те, чей адрес взят; и если адрес был взят, никто не мог использовать #define
или enum
(без адреса) ... поэтому я действительно не вижу, какую альтернативу можно было бы использовать. Если вы можете покончить с «оценкой времени компиляции», вы можете искать extern const
вместо этого.
#if
может быть предпочтительнее #ifdef
для логических флагов, но в данном случае это сделало бы невозможным определить , var
как 0
из командной строки. Так что в этом случае, #ifdef
имеет больше смысла, поскольку 0
это законное значение для var
.
В 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 ++ нет необходимости явно указывать).
const int
объектов в case-label запрещено во всех версиях языка Си. (Разумеется, ваш компилятор может поддерживать его как нестандартное расширение на языке C ++.)
const
означает только для чтения. const int r = rand();
совершенно законно.
constexpr
чем в случае const
с stl
контейнерами типа array
or bitset
.
switch()
утверждении, а не в case
одном. Я тоже попался на этом ☺
Разница между static const
и #define
заключается в том, что первый использует память, а второй не использует память для хранения. Во-вторых, вы не можете передать адрес a, #define
тогда как вы можете передать адрес a static const
. На самом деле, в зависимости от того, в каких обстоятельствах мы находимся, нам нужно выбрать одно из этих двух. Оба в своих лучших проявлениях при разных обстоятельствах. Пожалуйста, не думайте, что одно лучше другого ... :-)
Если бы это было так, Деннис Ричи оставил бы лучшего в покое ... хахаха ... :-)
const
действительно использует память. GCC (протестированный с 4.5.3 и несколькими более новыми версиями) легко оптимизирует const int
прямой код в вашем коде при использовании -O3. Таким образом, если вы занимаетесь разработкой встроенных систем с небольшим объемом ОЗУ (например, AVR), вы можете безопасно использовать C-константы, если используете GCC или другой совместимый компилятор. Я не проверял это, но ожидаю, что Clang сделает то же самое, кстати.
В Си #define
гораздо популярнее. Вы можете использовать эти значения для объявления размеров массива, например:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
static const
Насколько мне известно, ANSI C не позволяет вам использовать s в этом контексте. В C ++ вы должны избегать макросов в этих случаях. Ты можешь написать
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
и даже опустить, static
потому что внутренняя связь подразумевается const
уже [только в C ++].
const int MY_CONSTANT = 5;
в одном файле и доступ к нему extern const int MY_CONSTANT;
в другом. Я не смог найти в стандарте никакой информации (по крайней мере, C99) об const
изменении поведения по умолчанию: «6.2.2: 5 Если объявление идентификатора для объекта имеет область видимости файла и не имеет спецификатора класса хранения, его связь является внешней».
bar
является VLA (массив переменной длины); компилятор может генерировать код, как если бы его длина была постоянной.
Еще одним недостатком 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 uint8_t const ARRAY_SIZE = 16;
внезапно перестали компилироваться, может быть немного сложнее, особенно когда они #define ARRAY_SIZE 256
зарыты в десять слоев глубоко в запутанной паутине заголовков. Это имя всех заглавных букв ARRAY_SIZE
напрашивается на неприятности. Зарезервируйте ALL_CAPS для макросов и никогда не определяйте макрос, который не находится в форме ALL_CAPS.
const
. Это может быть проголосовано больше!
Если вы можете сойти с рук, static const
имеет много преимуществ. Он подчиняется обычным принципам области видимости, отображается в отладчике и в целом подчиняется правилам, которым подчиняются переменные.
Тем не менее, по крайней мере, в оригинальном стандарте C он не является константой. Если вы используете #define var 5
, вы можете написать int foo[var];
как объявление, но вы не можете сделать это (кроме как как расширение компилятора) с static const int var = 5;
. Это не так в C ++, где static const
версия может использоваться везде , где может #define
версия, и я считаю, что это также случай с C99.
Однако никогда не называйте #define
константу строчными буквами. Он будет отменять любое возможное использование этого имени до конца модуля перевода. Макросконстанты должны находиться в том, что фактически является их собственным пространством имен, которое традиционно состоит из заглавных букв, возможно, с префиксом.
const
в С99 это еще не настоящая константа. Вы можете объявить размер массива с помощью const
C99, но только потому, что C99 поддерживает массивы переменной длины. По этой причине он будет работать только там, где разрешены VLA. Например, даже в C99 вы все еще не можете использовать const
для объявления размера массива-члена в struct
.
const int
размером, как если бы это был константа C ++ или макрос. Если вы хотите зависеть от этого отклонения GCC от стандарта, это, конечно, ваш выбор, я бы лично согласился с этим, если вы не можете на самом деле предвидеть использование другого компилятора, кроме GCC или Clang, последний обладает той же функцией здесь (протестировано с Clang). 3.7).
ВСЕГДА предпочтительно использовать 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 дает предупреждение.
Определение
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 CONST 5
, if (CONST == 5) { do_this(); } else { do_that(); }
а компилятор удаляет else
ветку. Как вы предлагаете редактировать машинный код, чтобы изменить CONST
на 6?
#define
не пуленепробиваемый.
#define
. Единственный реальный способ сделать это - отредактировать исходный код и перекомпилировать.
Хотя вопрос был о целых числах, стоит отметить, что #define и перечисления бесполезны, если вам нужна постоянная структура или строка. Они оба обычно передаются функциям как указатели. (Со строками это требуется; со структурами это намного более эффективно.)
Что касается целых чисел, если вы находитесь во встроенной среде с очень ограниченным объемом памяти, вам может потребоваться беспокоиться о том, где хранится константа и как компилируется доступ к ней. Компилятор может добавить две константы во время выполнения, но добавить две #defines во время компиляции. Константа #define может быть преобразована в одну или несколько MOV [немедленных] инструкций, что означает, что константа эффективно сохраняется в памяти программы. Константа const будет храниться в разделе .const в памяти данных. В системах с Гарвардской архитектурой могут быть различия в производительности и использовании памяти, хотя они, вероятно, будут небольшими. Они могут иметь значение для оптимизации внутренних циклов.
Не думаю, что есть ответ на вопрос «что всегда лучше», но, как сказал Матье
static const
Тип безопасен. Моя самая большая неприятность с тем #define
, что при отладке в Visual Studio вы не можете наблюдать за переменной. Это дает ошибку, что символ не может быть найден.
Между прочим, альтернативой #define
, которая обеспечивает правильную область видимости, но ведет себя как «настоящая» константа, является «enum». Например:
enum {number_ten = 10;}
Во многих случаях полезно определять перечисляемые типы и создавать переменные этих типов; если это сделано, отладчики могут отображать переменные в соответствии с именем перечисления.
Однако следует сделать одно важное предостережение: в C ++ перечисляемые типы имеют ограниченную совместимость с целыми числами. Например, по умолчанию нельзя выполнять арифметику с ними. Я считаю это странным поведением по умолчанию для перечислений; хотя было бы неплохо иметь тип "строгого перечисления", учитывая желание иметь C ++, в целом совместимый с C, я бы подумал, что поведение по умолчанию для типа "enum" должно быть взаимозаменяемым с целыми числами.
int
, поэтому «enum hack» не может использоваться с другими целочисленными типами. (Перечисление типа совместит с некоторым определяемой реализацией целого типа, не обязательно int
, но в этом случае типа является анонимным , так что не имеет значения.)
int
переменной с типом перечисления (какие компиляторы могут это делать), и каждый пытается присвоить такую переменную член собственного перечисления. Хотелось бы, чтобы комитеты по стандартам добавили переносимые способы объявления целочисленных типов с заданной семантикой. ЛЮБАЯ платформа, независимо от char
размера, должна иметь возможность, например, объявлять тип, который обернет мод 65536, даже если компилятор должен добавить множество AND R0,#0xFFFF
или эквивалентные инструкции.
uint16_t
, хотя, конечно, это не тип перечисления. Было бы неплохо позволить пользователю указать целочисленный тип, используемый для представления данного типа перечисления, но вы можете добиться почти такого же эффекта с помощью typedef
for uint16_t
и ряда #define
s для отдельных значений.
2U < -1L
как истинные, а другие как ложные, и теперь мы застряли с тем фактом, что некоторые платформы будут осуществлять сравнение между uint32_t
и int32_t
как подписанные а некоторые как неподписанные, но это не означает, что Комитет не может определить совместимого вверх преемника C, который включает типы, семантика которых будет согласована на всех компиляторах.
Простая разница:
Во время предварительной обработки константа заменяется ее значением. Таким образом, вы не можете применить оператор разыменования к определению, но вы можете применить оператор разыменования к переменной.
Как и следовало ожидать, определить быстрее, чем статическое 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 статические константы размещаются в текстовом сегменте программы.
Выше я хотел рассказать об операторе ссылки, поэтому замените разыменование ссылкой.
const
классификатора. C не имеет символьных констант, кроме перечислимых . А const int
является переменной. Вы также путаете язык и конкретные реализации. Там нет требования, где разместить объект. И это даже не верно для gcc: обычно это помещает const
квалифицированные переменные в .rodata
раздел. Но это зависит от целевой платформы. А ты имеешь ввиду адрес оператора &
.
Мы рассмотрели полученный ассемблерный код на MBF16X ... Оба варианта приводят к одному и тому же коду для арифметических операций (например, ADD Immediate).
Так const int
что предпочтительнее для проверки типов, пока #define
это старый стиль. Может быть, это зависит от компилятора. Так что проверьте ваш созданный код ассемблера
Я не уверен, прав ли я, но, по моему мнению, вызов #define
значения d намного быстрее, чем вызов любой другой обычно объявленной переменной (или константного значения). Это потому, что когда программа работает и ей нужно использовать некоторую обычно объявленную переменную, ей нужно перейти в точное место в памяти, чтобы получить эту переменную.
Напротив, когда используется #define
значение d, программе не нужно переходить к какой-либо выделенной памяти, она просто принимает значение. Если #define myValue 7
и программа вызывает myValue
, она ведет себя точно так же, как и при простом вызове 7
.