Який з них краще використовувати серед наведених нижче тверджень у С?
static const int var = 5;
або
#define var 5
або
enum { var = 5 };
Який з них краще використовувати серед наведених нижче тверджень у С?
static const int var = 5;
або
#define var 5
або
enum { var = 5 };
Відповіді:
Це залежить від того, для чого вам потрібне значення. Ви (і всі інші поки що) опустили третю альтернативу:
static const int var = 5;
#define var 5
enum { var = 5 };
Ігноруючи питання щодо вибору імені, тоді:
Тож у більшості контекстів віддають перевагу "перерахунку" над альтернативами. В іншому випадку перша і остання точки кулі, ймовірно, будуть контролюючими факторами - і вам доведеться думати важче, якщо вам потрібно задовольнити обидва відразу.
Якщо ви запитували про C ++, то ви кожен раз використовували б варіант (1) - статичний const.
enum
є те, що вони реалізовані як int
([C99] 6.7.2.2/3). #define
Дозволяє визначити непідписані і довго з U
і L
суфікси, і const
дозволяє дати тип. enum
може спричинити проблеми з перетвореннями звичайного типу.
enum
ні #define
використовуйте додатковий простір. Значення відображатиметься в об'єктному коді як частина інструкцій, а не виділяється на зберігання в сегменті даних або в купі або в стеку. У вас буде виділено трохи місця для static const int
, але компілятор може оптимізувати його, якщо ви не знайдете адресу.
enum
s (і static const
): їх неможливо змінити. a define
може бути #undefine
'd, де 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 правильна відповідь: використовувати #define
(або, якщо потрібно, enum
)
Незважаючи на те, що корисно мати властивості розміщення та введення тексту в const
об'єкті, насправді const
об'єкти в 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
об'єктів у регістрі мови є незаконним у всіх версіях мови C. (Звичайно, ваш компілятор може підтримувати це як нестандартне розширення мови на C ++.)
const
означає лише для читання const int r = rand();
є абсолютно законним.
constexpr
порівняно const
з stl
контейнерами типу array
або bitset
.
switch()
заяві, а не в case
одній. Мене щойно зачепило за це ☺
Різниця між static const
і #define
полягає в тому, що перший використовує пам'ять, а пізніший не використовує пам'ять для зберігання. По-друге, ви не можете передавати адресу, #define
тоді як ви можете передавати адресу static const
. Насправді, залежно від того, під якою обставиною ми знаходимось, нам потрібно вибрати одну з цих двох. Обидва в найкращих випадках за різних обставин. Будь ласка, не вважайте, що один кращий за інший ... :-)
Якби це було так, Денніс Річі залишив би найкращого в спокої ... ха-ха ... :-)
const
дійсно використовує пам'ять. GCC (тестований з 4.5.3 та кількома новішими версіями) легко оптимізує const int
прямий літерал у вашому коді при використанні -O3. Отже, якщо ви займаєтеся розробкою вбудованої оперативної пам'яті з низьким рівнем оперативної пам'яті (наприклад, AVR), ви можете сміливо використовувати C consts, якщо ви використовуєте GCC або інший сумісний компілятор. Я не перевіряв цього, але очікую, що Кланг зробить те ж саме, до речі.
У С #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 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
у C99 все ще не є реальною константою. Ви можете оголосити розмір масиву за допомогою const
C у C99, але тільки тому, що C99 підтримує масиви змінної довжини. З цієї причини він працюватиме лише там, де дозволені VLA. Наприклад, навіть у C99 ви все одно не можете використовувати a const
для оголошення розміру масиву члена у struct
.
const int
розміром, як якщо б це був C ++ const або макрос. Незалежно від того, чи хочете ви залежати від цього відхилення 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
^
Зауважте, що перерахунок дає помилку, коли визначення визначає попередження.
Визначення
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 та enums марні, якщо вам потрібна константна структура чи рядок. Обидва вони передаються функціям як покажчики. (Для струн це потрібно; для структур це набагато ефективніше.)
Що стосується цілих чисел, якщо ви перебуваєте у вбудованому середовищі з дуже обмеженою пам’яттю, вам може знадобитися турбуватися про те, де зберігається константа та як збирається доступ до неї. Компілятор може додати два consts під час виконання, але додати два #defines під час компіляції. Константа #define може бути перетворена в одну або кілька інструкцій MOV [негайних], що означає, що константа ефективно зберігається в пам'яті програми. Константа const буде збережена в розділі .const в пам'яті даних. У системах з гарвардською архітектурою можуть бути відмінності у продуктивності та використанні пам’яті, хоча вони, ймовірно, невеликі. Вони можуть мати значення для жорсткої оптимізації внутрішніх циклів.
Не думайте, що є відповідь "що завжди найкраще", але, як сказав Матьє
static const
є безпечним для типу. #define
Хоча мій найбільший улюбленець домашнього улюбленця , при налагодженні у Visual Studio ви не можете спостерігати за змінною. Це дає помилку, що символ неможливо знайти.
Між іншим, альтернативою #define
, яка забезпечує належне оцінювання, але поводиться як "справжня" константа, є "перерахунок". Наприклад:
enum {number_ten = 10;}
У багатьох випадках корисно визначати перелічені типи та створювати змінні цих типів; якщо це зроблено, налагоджувачі можуть мати змогу відображати змінні відповідно до назви їх перерахування.
Однак важливий застереження щодо цього: у C ++ перелічені типи мають обмежену сумісність із цілими числами. Наприклад, за замовчуванням не можна виконувати арифметику над ними. Я вважаю це цікавою поведінкою за замовчуванням для переліків; хоча було б непогано мати тип "суворого перерахунку", враховуючи бажання мати C ++, як правило, сумісний із C, я вважаю, що поведінка типу "enum" за замовчуванням має бути взаємозамінною з цілими числами.
int
, тому "haum enum" не можна використовувати з іншими цілими типами. (Перерахування типу поєднає з деяким визначається реалізацією цілого типу, не обов'язково int
, але в цьому випадку типу є анонімним , так що не має значення.)
int
змінної типу переліку (які компілятори дозволено робити) і намагається призначити такій змінній член власного перерахунку. Я хотів би, щоб комітети зі стандартів додали портативні способи оголошення цілих типів із заданою семантикою. БУДЬ-яка платформа, незалежно від char
розміру, повинна мати можливість, наприклад, оголосити тип, який охоплює мод 65536, навіть якщо компілятор повинен додати багато AND R0,#0xFFFF
чи еквівалентних інструкцій.
uint16_t
, хоча, звичайно, це не тип перерахування. Було б добре , щоб дозволити користувачеві вказати тип цілого числа , який використовується для подання даного типу перерахування, але ви можете багато чого домогтися того ж ефекту з typedef
для uint16_t
і серії #define
з для окремих значень.
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 виділяється в текстовому сегменті програми.
Вище я хотів розповісти про опорному операторі, тому замініть дереференцію на посилання.
const
класифікатора. C не має символічних констант, окрім констант . A const int
- змінна. Ви також плутаєте мову та конкретні реалізації. Немає вимоги, де розмістити об’єкт. І це навіть не стосується gcc: зазвичай він розміщує const
кваліфіковані змінні у .rodata
розділі. Але це залежить від цільової платформи. І ви маєте на увазі адресу оператора &
.
Ми розглянули створений код асемблера на MBF16X ... Обидва варіанти призводять до того ж коду для арифметичних операцій (наприклад, негайне додавання, наприклад).
Тому const int
вважається кращим для перевірки типу, поки #define
це старий стиль. Можливо, це специфічно для компілятора. Тому перевірте свій створений код асемблера.
Я не впевнений, чи маю рацію, але, на мою думку, виклик #define
значення d набагато швидше, ніж виклик будь-якої іншої нормально оголошеної змінної (або значення const). Це тому, що коли програма працює і їй потрібно використовувати якусь звичайно оголошену змінну, їй потрібно перейти на точне місце в пам'яті, щоб отримати цю змінну.
Навпаки, коли вона використовує #define
значення d, програмі не потрібно переходити до будь-якої виділеної пам'яті, вона просто приймає значення. Якщо #define myValue 7
і програма викликає myValue
, вона поводиться точно так само, як коли вона просто дзвонить 7
.