Во-первых, я обнаружил, что невозможно определить тип константы с помощью #define, почему?
Почему что? Это неправда:
#define MY_INT_CONSTANT ((int) 12345)
Во-вторых, есть ли преимущества в использовании одного из них перед другим?
Да. #define
определяет макрос, который заменяется еще до начала компиляции. const
просто изменяет переменную, чтобы компилятор пометил ошибку, если вы попытаетесь ее изменить. Есть контексты, в которых вы можете использовать a, #define
но не можете использовать const
(хотя я изо всех сил пытаюсь найти его, используя последнюю версию clang). Теоретически a const
занимает место в исполняемом файле и требует ссылки на память, но на практике это несущественно и может быть оптимизировано компилятором.
const
s гораздо более дружелюбны к компилятору и отладчику, чем #define
s. В большинстве случаев это главный момент, который вы должны учитывать при принятии решения, какой из них использовать.
Просто подумайте о контексте, в котором вы можете использовать, #define
но не можете const
. Если у вас есть константа, которую вы хотите использовать во многих .c
файлах, #define
вы просто вставляете ее в заголовок. С a у const
вас должно быть определение в файле C и
const int MY_INT_CONST = 12345;
extern const int MY_INT_CONST;
в шапке. MY_INT_CONST
не может использоваться как размер статического или глобального массива области видимости в любом файле C, кроме того, в котором он определен.
Однако для целочисленных констант вы можете использовать расширение enum
. Фактически, это то, что Apple делает почти всегда. Это имеет все преимущества как #define
s, так и const
s, но работает только для целочисленных констант.
enum
{
MY_INT_CONST = 12345,
};
Наконец, какой способ более эффективен и / или безопаснее?
#define
теоретически более эффективен, хотя, как я уже сказал, современные компиляторы, вероятно, обеспечивают небольшую разницу. #define
более безопасен тем, что попытка присвоить ему всегда является ошибкой компилятора
#define FOO 5
FOO = 6;
const
s можно обманом заставить быть назначенным, хотя компилятор может выдавать предупреждения:
const int FOO = 5;
(int) FOO = 6;
В зависимости от платформы назначение может по-прежнему завершаться ошибкой во время выполнения, если константа помещена в сегмент только для чтения, и это официально неопределенное поведение в соответствии со стандартом C.
Лично для целочисленных констант я всегда использую enum
s для констант других типов, я использую, const
если у меня нет очень веской причины не делать этого.