当前位置:  开发笔记 > 编程语言 > 正文

"static const"vs"#define"vs"enum"

如何解决《"staticconst"vs"#define"vs"enum"》经验,为你挑选了10个好方法。

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

static const int var = 5;

要么

#define var 5

要么

enum { var = 5 };

Jonathan Lef.. 660

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

    static const int var = 5;

    #define var 5

    enum { var = 5 };

忽略有关名称选择的问题,然后:

如果需要传递指针,则必须使用(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> Jonathan Lef..:

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

    static const int var = 5;

    #define var 5

    enum { var = 5 };

忽略有关名称选择的问题,然后:

如果需要传递指针,则必须使用(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.


精彩的清单!`enum`的一个缺点是它们被实现为`int`([C99] 6.7.2.2/3).`#define`允许你用`U`和`L`后缀指定unsigned和long,而`const`允许你给出一个类型.`enum`会导致常规类型转换出现问题.
(2)人们总是抱怨类型安全.我永远不明白为什么不只是使用"#define var((int)5)"并且使用define来获得类型安全性.
@QED:不,谢谢.一个简单的常数在括号外是安全的.或者,告诉我如何通过不在括号中使用5来改变合法预期编译的程序.如果它是函数式宏的参数,或者表达式中有任何运算符,那么如果我没有包含括号,那么你应该责怪我.但这不是这种情况.
另一个对'enum`s(和`static const`)的'投票':它们是无法改变的.`define`可以是``undefine`'d,其中`enum`和`static const`固定为给定的值.
@RedX:你必须处在一个非常特殊的环境中,才能让空间受到关注.也就是说,"enum"和"#define"本身都没有使用额外的空间.该值将作为指令的一部分出现在目标代码中,而不是在数据段或堆中或堆栈上分配存储.你将为`static const int`分配一些空间,但是如果你不接一个地址,编译器可能会优化它.
Yust一直在研究Arduino ...极小的内存空间(小于2 k)......选项2正在帮助很多.
最不安全的替代方案实际上可能是3).在这个例子中,标准的所有3个案例都是_guaranteed_,类型为`int`,长度为`sizeof(int)`.但是,枚举类型变量可以是任何类型,包括`char`,`signed int`和`unsigned int`.如果使用enum _constants_,那么使用enum _variables_也很诱人,但由于C标准(6.7.2.2)中的设计错误,这些错误可能不同!如果变量的大小很重要,则应避免使用枚举_variables_,因为它们不是类型安全的.
除了@Gauthier所说的,替代(1)和(2)也可以用于非整数常量.
为了爱一切圣洁,将你的#define值放在括号中
使用静态const的一个缺点是它们不能用于定义*其他*常量值.例如:`static const a = 5; static const b = a; //警告:在常量表达式中使用const变量在C`中是非标准的
@DanM.:在C(这个问题的主题)中,使用`constexpr`会产生语法错误.在C++中,您可以(并且可能应该)在适当的时候使用`constexpr`.我不确定我可以简单地确定`constexpr`应该优先使用`const`.
在空间人为环境中的内存使用情况如何?是否仅保证#define不占用任何额外空间?
@Groo不确定您的意思。在非古代版本的GCC / Clang中,当枚举在分配和函数调用中混合时,将调用-Wenum-conversion(默认启用或使用-Wall启用)。

2> Matthieu M...:

一般来说:

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__),那么你最好非常仔细地命名你的宏:在其命名约定中, Boost推荐所有大写字母,从项目名称开始(这里是BOOST_ ),在阅读库时,您会注意到(通常)后面跟着特定区域(库)的名称,然后是一个有意义的名称.

它通常会使冗长的名字:)


这是标准的C++传福音.下面的答案更清楚地解释了选项的真正含义和含义.特别是:我刚刚遇到"静态const"的问题.有人用它在头文件中定义了大约2000个"常量".然后这个头文件包含在大约100个".c"和".cpp"文件中.=> 8Mbytes为"consts".大.是的我知道您可以使用链接器删除未引用的consts,但是这仍然会留下引用的"consts".耗尽空间这个答案有什么不妥.
@Tim Post:对于布尔标志,`#if`可能优于`#ifdef`,但在这种情况下,它将无法从命令行将`var`定义为`0`.所以在这种情况下,`#ifdef`更有意义,只要`0`是`var`的合法值.
使用#if比使用#ifdef更好,但我不同意.+1.
同意 - 还有#define,因为预处理器不知道语法,所以存在修改代码的一般危险.
@IngoBlackman:有了一个好的编译器,只有那些地址被获取的静态文件应该保留;如果采用该地址,则可能不会使用#define或enum(无地址)...所以我真的看不到可以使用哪种替代方法。如果您可以取消“编译时间评估”,那么您可能正在寻找`extern const`。

3> AnT..:

在C中,特别是?在C中,正确答案是:使用#define(或者,如果适用enum)

虽然具有对象的作用域和键入属性是有益的,但const实际上constC 中的对象(与C++相反)不是真正的常量,因此在大多数实际情况下通常是无用的.

因此,在C中,选择应取决于您计划如何使用常量.例如,您不能将const int对象用作case标签(宏将起作用).您不能将const int对象用作位字段宽度(而宏将起作用).在C89/90中,您不能使用const对象来指定数组大小(宏将起作用).即使在C99中,const当您需要非VLA阵列时,也无法使用对象指定数组大小.

如果这对您很重要,那么它将决定您的选择.大多数时候,你别无选择,只能#define在C中使用.不要忘记另一种选择,它会在C中产生真正的常量enum.

在C++中,const对象是真正的常量,所以在C++中,最好更喜欢const变体(static尽管不需要在C++中显式).


"......*因此在大多数实际情况下通常都是无用的*." 我不同意.只要您不需要将名称用作常量表达式,它们就非常有用.C中的"常量"一词意味着可以在编译时进行评估; `const`表示只读.`const int r = rand();`完全合法.
@john:嗯,您需要提供您测试的代码并命名特定的编译器.在case-labels中使用`const int`对象在所有C语言版本中都是非法的.(当然,您的编译器可以自由地支持它作为非标准的C++语言扩展.)
"你不能使用const int对象作为case标签(当一个宏可以工作)"--->关于这个语句我测试了一个const int变量在C中的switch-case它正在工作....

4> wrapperm..:

static const和之间的区别在于#define前者使用内存而后者不使用内存进行存储.其次,你不能传递一个地址,#define而你可以传递一个地址static const.实际上,这取决于我们所处的环境,我们需要从这两者中选择一个.在不同情况下,两者都处于最佳状态.请不要认为一个比另一个好...... :-)

如果情况确实如此,丹尼斯·里奇 本来会保持最好的一个......哈哈哈...... :-)


提及内存的+1,一些嵌入式系统仍然没有那么多,虽然我可能会开始使用静态consts,如果需要只改为#defines.
我刚试过它.实际上,与#define或enum相比,const int使用了额外的内存.由于我们对嵌入式系统进行编程,因此无法承担额外的内存使用量.所以,我们将回到使用#define或enum.
实际上,"const"确实使用内存是不正确的.使用-O3时,GCC(使用4.5.3和一些较新版本测试)可以轻松地将`const int`优化为代码中的直接文字.因此,如果您使用低内存嵌入式开发(例如AVR),则可以安全地使用C consts,如果您使用GCC或其他兼容的编译器.我没有对它进行测试,但希望Clang做同样的事情顺便说一下.

5> sellibitze..:

在C #define中更受欢迎.您可以使用这些值来声明数组大小,例如:

#define MAXLEN 5

void foo(void) {
   int bar[MAXLEN];
}

static const据我所知,ANSI C不允许您在此上下文中使用s.在C++中,您应该避免在这些情况下使用宏.你可以写

const int maxlen = 5;

void foo() {
   int bar[maxlen];
}

static因为内部链接已经暗示const[仅限C++] ,所以甚至会遗漏.



6> Gauthier..:

constC中的另一个缺点是你不能在初始化另一个时使用该值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在这些情况下使用打字,否则......


游戏有点晚了,但这个问题出现在另一个问题中.追逐为什么你的`静态uint8_t const ARRAY_SIZE = 16;`突然不再编译可能有点挑战,特别是当`#define ARRAY_SIZE 256`在一个纠结的标题网中埋藏十层深.所有大写名称`ARRAY_SIZE`都在寻找麻烦.为宏保留ALL_CAPS,并且永远不要定义不是ALL_CAPS形式的宏.

7> David Thornl..:

如果你能逃脱它,static const有很多优点.它遵循正常的范围原则,在调试器中可见,并且通常遵循变量服从的规则.

但是,至少在最初的C标准中,它实际上并不是一个常数.如果你使用#define var 5,你可以写int foo[var];一个声明,但你不能这样做(除了作为编译器扩展"with static const int var = 5;.在C++中不是这种情况,static const版本可以在版本可以使用的任何地方使用#define,我相信这C99也是如此.

但是,永远不要#define用小写名称命名常量.在翻译单元结束之前,它将覆盖对该名称的任何可能使用.宏常量应该在它们自己的命名空间中,它通常都是大写字母,可能带有前缀.


不幸的是,C99并非如此.C99中的`const`仍然不是一个真正的常量.您可以在C99中使用`const`声明数组大小,但这只是因为C99支持可变长度数组.因此,它仅适用于允许VLA的情况.例如,即使在C99中,你仍然不能使用`const`来声明`struct`中成员数组的大小.

8> suren..:

始终最好使用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...



9> Non-maskable..:

#define var 5如果你有类似的东西,会给你带来麻烦mystruct.var.

例如,

struct mystruct {
    int var;
};

#define var 5

int main() {
    struct mystruct foo;
    foo.var = 1;
    return 0;
}

预处理器将替换它,代码将无法编译.出于这个原因,传统的编码风格表明所有常量都#define使用大写字母来避免冲突.



10> Michael Pott..:

我写了快速测试程序来证明一个区别:

#include 

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给出警告时,枚举会给出错误.

推荐阅读
吻过彩虹的脸_378
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有