在C中的以下陈述中哪一个更好用?
static const int var = 5;
或
#define var 5
或
enum { var = 5 };
答案 0 :(得分:634)
这取决于你需要什么价值。你(和其他人到目前为止)省略了第三种选择:
static const int var = 5;
#define var 5
enum { var = 5 };
忽略有关名称选择的问题,然后:
因此,在大多数情况下,更喜欢“枚举”而非替代品。否则,第一个和最后一个要点可能是控制因素 - 如果你需要同时满足这两个要点,你必须更加努力。
如果你问的是C ++,那么你每次都会使用选项(1) - 静态const。
答案 1 :(得分:267)
一般来说:
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__
),那么你最好小心地命名你的宏:naming convention { {3}}建议所有大写字母,从项目名称(此处为BOOST_)开始,在仔细阅读库时,您会注意到(通常)后面是特定区域(库)的名称,然后是有意义的名称
它通常会产生冗长的名称:)
答案 2 :(得分:105)
在C中,特别是?在C中,正确答案是:使用#define
(或者,如果适用,enum
)
虽然拥有const
对象的作用域和输入属性是有益的,但实际上C中的const
对象(而不是C ++)不是真正的常量,因此在大多数情况下通常都是无用的实际案例。
因此,在C语言中,选择应取决于您计划如何使用常量。例如,您不能将const int
对象用作case
标签(而宏将起作用)。您不能将const int
对象用作位字段宽度(而宏将起作用)。在C89 / 90中,您不能使用const
对象来指定数组大小(而宏将起作用)。即使在C99中,当您需要非VLA数组时,也无法使用const
对象来指定数组大小。
如果这对您很重要,那么它将决定您的选择。大多数时候,你别无选择,只能在C中使用#define
。不要忘记另一种在C中产生真常数的替代方法 - enum
。
在C ++中const
对象是真正的常量,所以在C ++中,最好更喜欢const
变体(在C ++中不需要显式的static
)。
答案 3 :(得分:29)
static const
和#define
之间的区别在于前者使用内存而后者不使用内存进行存储。其次,您无法传递#define
的地址,而您可以传递static const
的地址。实际上,这取决于我们所处的环境,我们需要从这两者中选择一个。在不同情况下,两者都处于最佳状态。请不要认为一个比另一个好......: - )
如果情况确实如此,Dennis Ritchie会保持最好的一个......哈哈哈......: - )
答案 4 :(得分:17)
在C #define
中更受欢迎。您可以使用这些值来声明数组大小,例如:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
据我所知,ANSI C不允许您在此上下文中使用static const
。在C ++中,您应该避免在这些情况下使用宏。你可以写
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
甚至遗漏static
,因为const
已经隐含了内部链接[仅限C ++]。
答案 5 :(得分:13)
C中const
的另一个缺点是您无法在初始化另一个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
,否则......
答案 6 :(得分:9)
如果你能逃脱它,static const
有很多好处。它遵循正常的范围原则,在调试器中可见,并且通常遵守变量服从的规则。
然而,至少在最初的C标准中,它实际上并不是常数。如果使用#define var 5
,则可以将int foo[var];
写为声明,但不能这样做(除了作为编译器扩展“with static const int var = 5;
。在C ++中不是这种情况, static const
版本可以在#define
版本可以使用的地方使用,我相信C99也是如此。
但是,永远不要用小写名称命名#define
常量。在翻译单元结束之前,它将覆盖对该名称的任何可能使用。宏常量应该在它们自己的命名空间中,它通常都是大写字母,可能带有前缀。
答案 7 :(得分:6)
我写了快速测试程序来证明一个区别:
#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
^
请注意,当define给出警告时,枚举会出错。
答案 8 :(得分:5)
#define var 5
等内容, mystruct.var
会给您带来麻烦。
例如,
struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
}
预处理器将替换它,代码将无法编译。出于这个原因,传统的编码风格表明所有常数#define
都使用大写字母来避免冲突。
答案 9 :(得分:5)
总是优先使用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...
答案 10 :(得分:4)
定义
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
是定义不能通过任何方式修改的常量值的唯一方法。
答案 11 :(得分:3)
不要认为“这总是最好”的答案,但正如Matthieu所说
static const
是类型安全的。不过,#define
的最大宠儿就是在Visual Studio调试时无法观察变量。它给出了无法找到符号的错误。
答案 12 :(得分:3)
顺便提一下,#define
的替代方案是“枚举”,它提供了适当的范围但行为类似于“真实”常量。例如:
enum {number_ten = 10;}
在许多情况下,定义枚举类型并创建这些类型的变量很有用;如果这样做,调试器可能能够根据其枚举名称显示变量。
但是,这样做有一个重要的警告:在C ++中,枚举类型与整数的兼容性有限。例如,默认情况下,不能对它们执行算术运算。我发现这是枚举的奇怪默认行为;虽然有一个“严格枚举”类型会很好,因为希望C ++通常与C兼容,我认为“枚举”类型的默认行为应该可以与整数互换。答案 13 :(得分:2)
虽然问题是关于整数,但值得注意的是,如果你需要一个常量结构或字符串,#define和enums是没用的。这些通常都作为指针传递给函数。 (使用字符串是必需的;使用结构时效率更高。)
对于整数,如果您处于内存非常有限的嵌入式环境中,则可能需要担心常量的存储位置以及如何编译对它的访问。编译器可能在运行时添加两个consts,但在编译时添加两个#defines。 #define常量可以转换为一个或多个MOV [立即]指令,这意味着常量有效地存储在程序存储器中。 const常量将存储在数据存储器的.const部分中。在具有哈佛架构的系统中,虽然它们可能很小,但在性能和内存使用方面可能存在差异。它们可能对内环的硬核优化很重要。
答案 14 :(得分:1)
一个简单的区别:
在预处理时,常量将替换为其值。 因此,您无法将取消引用运算符应用于定义,但您可以将取消引用运算符应用于变量。
正如您所想的那样,define比静态const更快。
例如,拥有:
#define mymax 100
你做不到printf("address of constant is %p",&mymax);
。
但是
const int mymax_var=100
你可以printf("address of constant is %p",&mymax_var);
。
更清楚的是,define在预处理阶段被其值替换,因此我们没有在程序中存储任何变量。我们只使用了使用define的程序文本段的代码。
但是,对于静态const,我们有一个在某处分配的变量。对于gcc,静态const在程序的文本段中分配。
上面,我想告诉引用运算符,所以用引用替换dereference。
答案 15 :(得分:0)
我们查看了MBF16X上生成的汇编程序代码......两种变体都会产生相同的算术运算代码(例如ADD Immediate)。
因此const int
是类型检查的首选,而#define
是旧样式。也许它是特定于编译器的。因此,请检查生成的汇编程序代码。
答案 16 :(得分:0)
我不确定我是否正确,但是我认为调用#define
d值比调用任何其他通常声明的变量(或const值)要快得多。
这是因为程序在运行时需要使用一些通常声明的变量,因此需要跳转到内存中的确切位置以获取该变量。
相反,当它使用#define
d值时,程序不需要跳转到任何已分配的内存,它只需要使用该值即可。如果#define myValue 7
且程序调用{{1}},则其行为与仅调用myValue
时的行为完全相同。