2010-02-22 3 views
42

В C, должен ли я определять константы по определению? В последнее время я читаю много кода, и все примеры сильно используют определения.Должен ли я предпочитать константы над определениями?

+17

Будьте осторожны с синдромом Стокгольма! – Potatoswatter

+0

[Этот ответ] (http://stackoverflow.com/a/1674459/2235567). –

+0

Возможный дубликат ["static const" vs "#define" в C] (http://stackoverflow.com/questions/1674032/static-const-vs-define-in-c) –

ответ

92

Нет, в общем случае вы не должны использовать объекты const, созданные в C, для создания констант имен. Чтобы создать с константой в C, вы должны использовать либо макросы (#define), либо перечисления. На самом деле, язык C не имеет констант, в том смысле, что вы, кажется, подразумеваете.(С значительно отличается от C++ в этом отношении)

В языке Си понятия постоянной и константного выражения определяются совершенно иначе, чем C++. В C константа означает a буквальное значение, как 123. Вот некоторые примеры констант в C

123 
34.58 
'x' 

Константа в C может быть использована для создания константных выражений. Тем не менее, поскольку объекты const-типа любого типа не являются константами в C, они не могут использоваться в постоянных выражениях и, следовательно, вы не можете использовать объекты с константой, в которых требуются постоянные выражения.

Например, следующее не является постоянной

const int C = 123; /* C is not a constant!!! */ 

и так как выше C не является постоянной, она не может быть использована, чтобы объявить тип массива в области видимости файла

typedef int TArray[C]; /* ERROR: constant expression required */ 

It не может использоваться в качестве метки корпуса

switch (i) { 
    case C: ; /* ERROR: constant expression required */ 
} 

Нельзя использовать как бит-fi Ширина поля

struct S { 
    int f : C; /* ERROR: constant expression required */ 
}; 

Он не может быть использована в качестве инициализатора для объекта с длительностью статического хранения

static int i = C; /* ERROR: constant expression required */ 

Он не может быть использован в качестве перечислимого инициализатора

enum { 
    E = C /* ERROR: constant expression required */ 
}; 

т.е. она не может быть использована где требуется константа.

Это может показаться контр-интуитивным, но это как C язык определен.

Вот почему вы видите эти многочисленные #define-s в коде, с которым работаете. Опять же, на языке C объект, имеющий const, имеет очень ограниченное использование. Они в основном совершенно бесполезны как «константы», поэтому на языке C вы в основном вынуждены использовать #define или перечисления для объявления истинных констант.

Конечно, в ситуациях, когда для вас работает объект с константой, то есть он делает то, что вы хотите, он действительно превосходит макросы по-разному, так как он ограничен и вводится в печать. Вероятно, вам следует предпочесть такие объекты, где это применимо, однако в общем случае вам придется учитывать вышеуказанные ограничения.

+16

+1 для понимания и правильной формулировки различий C/C++, которая кажется потерянной для многих людей. – asveikau

+1

Хороший ответ. Спасибо, что выделили проблему с помощью переменных 'const' для объявления размеров массива. –

+2

+1 Ничего себе, отличный ответ – helpermethod

9

Константы должны быть предпочтительнее, чем define s. Есть несколько преимуществ:

  • Тип безопасности. В то время как C является слабо типизированным языком, использование define теряет всю безопасность типа, что позволит компилятору подобрать проблемы для вас.

  • Простота отладки. Вы можете изменить значение констант через отладчик, в то время как define s автоматически преобразуются в коде препроцессором в фактическое значение , что означает, что если вы хотите изменить значение для целей тестирования/отладки, вы необходимо перекомпилировать.

+2

Я обнаружил, что многие компиляторы будет выпекать 'const static ints' в непосредственные константы opcode, как и #defines, что делает невозможным изменение в отладчике. – Crashworks

+0

@Crashworks - будет ли это зависеть от уровня оптимизации? – LeopardSkinPillBoxHat

+0

даже с оптимизацией компилятора отключена? –

1

Если это программно не определено, я использую #define. Например, если я хочу, чтобы все мои объекты пользовательского интерфейса имели одинаковое пространство между ними, я мог бы использовать #define kGUISpace 20.

2

Определения были частью языка длиннее констант, поэтому многие старые коды будут использовать их, потому что определяет, где единственный способ получить работу, когда код был написан. Для более позднего кода это может быть просто вопрос программиста.

Константы имеют тип, а также значение, поэтому они предпочли бы, когда имеет смысл для вашего значения иметь тип, но не тогда, когда он является безликим (или полиморфным).

7

Возможно, я использовал их неправильно, но, по крайней мере, в gcc, вы не можете использовать константы в операторах case.

const int A=12; 
switch (argc) { 
    case A: 
    break; 
} 
3

определить можно использовать для многих целей (очень свободно), и его следует избегать, если вы можете заменить, что с сопзЬ, которые определяют переменную, и вы можете сделать гораздо больше с ним.

В таких случаях ниже, определяет должен быть использован

  • директива переключатель
  • замена на исходные линии
  • кода макросов

пример, где вы должны использовать определить более const, когда у вас есть номер версии say 3, и вы хотите, чтобы версия 4 включала некоторые методы, которые недоступны в версии 3

#define VERSION 4 
... 

#if VERSION==4 
    ................ 
#endif 
5

Многие люди здесь дают вам совет «C++ style». Некоторые даже говорят, что аргументы C++ применимы к C. Это может быть справедливым моментом. (Является ли это или нет чувствует себя субъективным.) Люди, которые говорят, что const иногда означают, что что-то другое на двух языках тоже правильно.

Но это в основном второстепенные моменты и лично, я думаю, что в действительности есть относительно незначительные последствия для движения в любом случае.Это вопрос стиля, и я думаю, что разные группы людей дадут вам разные ответы.

С точки зрения общего использования, исторического использования и наиболее распространенного стиля, в C, более типично видеть #define. Использование C++-isms в C-коде может быть столь же странным для некоторого узкого сегмента C-кодеров. (В том числе и меня, так что там лежат мои предубеждения.)

Но я удивлен, что никто не предложил решение средней точки, которое «чувствует себя правильно» на обоих языках: если оно вписывается в группу целочисленных констант, используйте a enum.

1

Помимо превосходных причин, которые AndreyT использует для использования DEFINES, а не констант в коде «C», существует еще одна прагматическая причина использования DEFINES.

DEFINES легко определить и использовать файлы заголовков (.h), в которых любой опытный C-кодер будет искать найденные константы. Определение consts в заголовочных файлах не так просто - его больше кода, чтобы избежать дублирования определений и т. Д.

Также аргументы «typafe» являются спорными. Большинство компиляторов будут принимать вопиющие ошибки, такие как получение строки и int или , будет «делать правильную вещь» при небольшом рассогласовании, таком как присвоение целому числу float.

1

Макросы (определяет) могут использоваться препроцессором, а во время компиляции константы не могут.

Вы можете выполнить проверки времени компиляции, чтобы убедиться, что макрос находится в допустимом диапазоне (и #error или #fatal, если это не так). Вы можете использовать значения по умолчанию для макроса, если они еще не определены. Вы можете использовать макрос в размере массива.

компилятор может оптимизировать с помощью макросов лучше, чем он может с константами:

const int SIZE_A = 15; 
#define SIZE_B 15 

for (i = 0; i < SIZE_A + 1; ++i); // if not optimized may load A and add 1 on each pass 
for (i = 0; i < SIZE_B + 1; ++i); // compiler will replace "SIZE_B + 1" with 16 

Большой частью моей работы со встроенными процессорами, которые не имеют удивительный оптимизирующий компилятор. Возможно, gcc будет рассматривать SIZE_A как макрос на определенном уровне оптимизации.

5

Хотя этот вопрос относится только к C, я предполагаю, что это хорошо, чтобы знать это:

#include<stdio.h> 
int main() { 
    const int CON = 123; 
    int* A = &CON; 
    (*A)++; 
    printf("%d\n", CON); // 124 in C 
} 

работает в C, но не в C++

Одна из причин использования #define следует избегать таких вещей, чтобы испортить ваш код, особенно это сочетание C и C++.

+0

+1 Хороший отзыв, не знал этого. – helpermethod

+1

Не использует указатели для изменения только для чтения ints в C неопределенном поведении? –

Смежные вопросы