“static const”vs“#define”vs“enum”

时间:2009-11-04 14:19:24

标签: c constants

在C中的以下陈述中哪一个更好用?

static const int var = 5;

#define var 5

enum { var = 5 };

17 个答案:

答案 0 :(得分:634)

这取决于你需要什么价值。你(和其他人到目前为止)省略了第三种选择:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };
  4. 忽略有关名称选择的问题,然后:

    • 如果您需要传递指针,则必须使用(1)。
    • 因为(2)显然是一个选项,所以你不需要传递指针。
    • (1)和(3)都在调试器的符号表中有一个符号 - 这使得调试更容易。 (2)更有可能没有符号,让你想知道它是什么。
    • (1)不能用作全局范围内数组的维度; (2)和(3)都可以。
    • (1)不能在函数范围内用作静态数组的维数; (2)和(3)都可以。
    • 在C99下,所有这些都可用于本地阵列。从技术上讲,使用(1)意味着使用VLA(可变长度数组),尽管'var'引用的维度当然会固定为5。
    • (1)不能在switch语句之类的地方使用; (2)和(3)都可以。
    • (1)不能用于初始化静态变量; (2)和(3)都可以。
    • (2)可以更改您不想更改的代码,因为它由预处理器使用; (1)和(3)都不会产生意想不到的副作用。
    • 您可以检测是否已在预处理器中设置了(2); (1)和(3)都不允许这样做。

    因此,在大多数情况下,更喜欢“枚举”而非替代品。否则,第一个和最后一个要点可能是控制因素 - 如果你需要同时满足这两个要点,你必须更加努力。

    如果你问的是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时的行为完全相同。