Який з них краще використовувати серед наведених нижче тверджень у С?
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 5enum { var = 5 };Ігноруючи питання щодо вибору імені, тоді:
Тож у більшості контекстів віддають перевагу "перерахунку" над альтернативами. В іншому випадку перша і остання точки кулі, ймовірно, будуть контролюючими факторами - і вам доведеться думати важче, якщо вам потрібно задовольнити обидва відразу.
Якщо ви запитували про C ++, то ви кожен раз використовували б варіант (1) - статичний const.
enumє те, що вони реалізовані як int([C99] 6.7.2.2/3). #defineДозволяє визначити непідписані і довго з Uі Lсуфікси, і constдозволяє дати тип. enumможе спричинити проблеми з перетвореннями звичайного типу.
enumні #defineвикористовуйте додатковий простір. Значення відображатиметься в об'єктному коді як частина інструкцій, а не виділяється на зберігання в сегменті даних або в купі або в стеку. У вас буде виділено трохи місця для static const int, але компілятор може оптимізувати його, якщо ви не знайдете адресу.
enums (і 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 consts в цьому контексті, наскільки я знаю. У 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 (масив змінної довжини); компілятор, ймовірно, генерує код так, ніби його довжина була постійною.
Ще одним недоліком constC є те, що ви не можете використовувати значення для ініціалізації іншого 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 все ще не є реальною константою. Ви можете оголосити розмір масиву за допомогою constC у 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;
}
Препроцесор замінить його, і код не буде компілюватися. З цієї причини традиційний стиль кодування пропонує всім постійним #defines використовувати великі літери, щоб уникнути конфлікту.
Я написав швидку програму тестування, щоб продемонструвати одну різницю:
#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.