“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:

  1. static const int var = 5;
  2. #define var 5
  3. 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 ++中,最好selectconstvariables(不需要C ++中的显式staticvariables)。

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