“static const”vs“#define”vs“enum”
在C中的下面的语句中哪一个更好用?
static const int var = 5;
要么
#define var 5
要么
enum { var = 5 };
一般来说:
static const
因为它尊重范围并且是types安全的。
唯一的警告我可以看到:如果你想variables可能定义在命令行上。 还有一个select:
#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
只要有可能,而不是macros/省略号,请使用types安全的替代方法。
如果你真的需要使用macros(例如,你想要__FILE__
或__LINE__
),那么你最好仔细命名你的macros:在它的命名约定中, Boostbuild议所有大写,从项目名称开始这里BOOST_),而仔细阅读库,你会注意到这是(通常)后面是特定区域(库)的名称,然后用一个有意义的名字。
它通常使冗长的名字:)
这取决于你需要什么价值。 你(和其他人迄今为止)省略了第三种select:
-
static const int var = 5;
-
#define var 5
-
enum { var = 5 };
忽略关于select名称的问题,那么:
- 如果你需要传递一个指针,你必须使用(1)。
- 由于(2)显然是一个选项,你不需要传递指针。
- (1)和(3)在debugging器的符号表中都有一个符号 – 这使得debugging更容易。 (2)更有可能没有象征,让你想知道它是什么。
- (1)不能用作全球范围数组的维度; (2)和(3)都可以。
- (1)不能用作函数范围内静态数组的维度; (2)和(3)都可以。
- 在C99下,所有这些都可以用于本地arrays。 从技术上讲,使用(1)意味着使用VLA(可变长度数组),尽pipe由'var'引用的维度当然会固定为5。
- (1)不能用在switch语句的地方; (2)和(3)都可以。
- (1)不能用来初始化静态variables; (2)和(3)都可以。
- (2)可以改变你不想改变的代码,因为它被预处理器使用; (1)和(3)都不会有这样的意想不到的副作用。
- 您可以检测(2)是否已经在预处理器中设置; (1)和(3)都不允许。
所以,在大多数情况下,更喜欢“枚举”。 否则,第一个和最后一个要点可能是控制因素 – 如果你需要同时满足这两个要点,你必须更加努力地思考。
如果你问的是C ++,那么每次都使用选项(1) – 静态常量。
在C中,具体是? 在C中,正确的答案是:使用#define
(或者,如果合适, enum
)
虽然有一个const
对象的范围和打字属性是有益的,但实际上C(而不是C ++)中的const
对象不是真正的常量,因此在大多数实际情况下通常是无用的。
所以,在C中,select应该取决于你打算如何使用常数。 例如,你不能使用一个const int
对象作为一个case
标签(而macros将工作)。 不能使用const int
对象作为位域宽度(而macros将起作用)。 在C89 / 90中,你不能使用一个const
对象来指定一个数组的大小(而macros将起作用)。 即使在C99中,当需要非VLA数组时,也不能使用const
对象来指定数组大小。
如果这对你很重要,那么它将决定你的select。 大多数情况下,你将别无select,只能在C中使用#define
。不要忘记另一种替代方法,它会在C – enum
中产生真正的常量。
在C ++中, const
对象是常量,所以在C ++中,最好selectconst
variables(不需要C ++中的显式static
variables)。
static const
和#define
的区别在于前者使用内存,后者不使用内存进行存储。 其次,你不能传递#define
的地址,而你可以传递一个static const
的地址。 其实这取决于我们在什么情况下,我们需要从这两个中选一个。 两者在不同情况下都处于最佳状态。 请不要以为一个比另一个好… 🙂
如果是这样的话, 丹尼斯·里奇本来会保持最好的一个……哈哈哈…… 🙂
在C#中定义更受欢迎。 您可以使用这些值来声明数组大小,例如:
#define MAXLEN 5 void foo(void) { int bar[MAXLEN]; }
就我所知,ANSI C不允许你在这个上下文中使用static const
。 在C ++中,你应该避免在这些情况下使用macros。 你可以写
const int maxlen = 5; void foo() { int bar[maxlen]; }
甚至省去了static
因为内部链接已经被const
暗示了[只在C ++中]。
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!
我很乐意在这些情况下使用typesconst
,否则…
如果你能摆脱它, static const
有很多优点。 它遵循正常的范围原则,在debugging器中可见,并且通常服从variables服从的规则。
但是,至less在原来的C标准中,它并不是一个常数。 如果使用#define var 5
,则可以写入int foo[var];
作为一个声明,但你不能这样做(除了作为一个编译器扩展“ static const int var = 5;
这不是在C ++的情况下, static const
版本可以在任何地方#define
版本可以使用,我相信C99也是如此。
但是,请勿使用小写名称命名#define
常量。 它将覆盖该名称的任何可能的使用,直到翻译单元结束。 macros常量应该是在他们自己的名字空间,这是传统的所有大写字母,也许有一个前缀。
如果你有像mystruct.var
这样的东西, mystruct.var
#define var 5
会给你带来麻烦。
例如,
struct mystruct { int var; }; #define var 5 int main() { struct mystruct foo; foo.var = 1; return 0; }
预处理器将replace它,代码将不会编译。 出于这个原因,传统的编码风格build议所有常量#define
使用大写字母来避免冲突。
我写了快速testing程序来演示一个区别:
#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 ^
请注意,当定义给出警告时,enum会给出错误。
不要以为“哪个总是最好”的答案是正确的,正如Matthieu所说
static const
是types安全的。 不过,我最大的宠儿是#define
,在Visual Studio中debugging时,你无法看到variables。 它给出了一个符号找不到的错误。
顺便提一句,# #define
的替代方法是“枚举”,它提供了正确的范围,但是却像“真正的”常量一样。 例如:
enum {number_ten = 10;}
在很多情况下,定义枚举types并创build这些types的variables是很有用的。 如果这样做了,debugging器可以根据枚举名称显示variables。
但是,一个重要的警告就是:在C ++中,枚举types与整数的兼容性有限。 例如,默认情况下,不能对它们进行算术运算。 我觉得这是一个好奇的默认行为, 虽然有一个“严格的枚举”types是很好的,考虑到C ++通常与C兼容的愿望,我会认为一个“枚举”types的默认行为应该与整数交换。
定义
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
是定义一个不能用任何方法修改的常量值的唯一方法。
总是最好使用const,而不是#define。 这是因为const由编译器处理,而预处理器处理#define。 这就像#define本身不是代码的一部分(粗略地说)。
例:
#define PI 3.1416
符号名称PI可能永远不会被编译器看到; 在源代码甚至到达编译器之前,它可能被预处理器删除。 结果,名称PI可能不会被input到符号表中。 如果在使用常量的编译过程中遇到错误,这可能会引起混淆,因为错误消息可能涉及3.1416而不是PI。 如果PI是在你没有写的头文件中定义的,那么你不知道3.1416是从哪里来的。
这个问题也可能会在符号debugging器中出现,因为再次,您正在编程的名称可能不在符号表中。
解:
const double PI = 3.1416; //or static const...
一个简单的区别:
在预处理时间,常量被replace为其值。 因此,您无法将解引用运算符应用于定义,但可以将解引用运算符应用于variables。
正如你所想的那样,静态常量的定义速度更快。
例如,具有:
#define mymax 100
你不能做printf("address of constant is %p",&mymax);
。
但是有
const int mymax_var=100
你可以做printf("address of constant is %p",&mymax_var);
。
更清楚的是,定义在预处理阶段被其值取代,所以我们没有任何存储在程序中的variables。 我们只是使用定义的程序的文本段中的代码。
但是,对于静态常量,我们有一个分配在某处的variables。 对于gcc,静态常量被分配在程序的文本段中。
上面,我想告诉参考运算符,所以用参考replace解除引用。
我们查看了在MBF16X上生成的汇编代码…这两种变体都会导致相同的算术运算代码(例如ADD Immediate)。
所以const int
是types检查的首选,而#define
是旧的风格。 也许它是编译器特定的。 所以检查你生成的汇编代码。
我已经看到用于控制常量范围的#define方法,因此模仿C ++中的私有类variables。
#define MAXL 4 // some code #undef MAXL