提问



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


static const int var = 5;


要么


#define var 5


要么


enum { var = 5 };

最佳参考


一般来说:


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_),在仔细阅读库时,您会注意到(通常)后面是特定区域(库)的名称,然后是一个有意义的名称。[68] [69]]]


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

其它参考1


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



  1. static const int var = 5;

  2. #define var 5

  3. 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。

其它参考2


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


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


因此,在C中,选择应该取决于您计划如何使用常量。例如,你不能使用const int对象作为case标签(当宏工作时)。你不能使用const int对象作为位域宽度(而宏将工作)。在C89/90中,你不能使用const对象来指定一个数组大小(当宏工作时)。即使在C99中你也不能使用const对象指定一个数组大小你需要一个非VLA阵列。[70]


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


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

其它参考3


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


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

其它参考4


在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


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


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


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


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

其它参考7


#define var 5如果你有mystruct.var这样的话会给你带来麻烦。


例如,


struct mystruct {
    int var;
};

#define var 5

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


预处理器将替换它并且代码不会被编译。因此,传统的编码风格表明所有常量#define都使用大写字母来避免冲突。

其它参考8


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


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

其它参考9


不要认为这总是最好的答案,但正如Matthieu所说的那样


static const


是类型安全的。不过,#define我最大的烦恼是在Visual Studio中调试时无法观察变量。它给出了一个无法找到符号的错误。[72]

其它参考10


顺便提一下,#define的替代方案是枚举,它提供适当的范围但行为类似于真实常数。例如:


enum {number_ten = 10;}


在许多情况下,定义枚举类型并创建这些类型的变量很有用;如果这样做,调试器可能能够根据其枚举名称显示变量。


但是,这样做有一个重要的警告:在C ++中,枚举类型与整数的兼容性有限。例如,默认情况下,无法对它们执行算术运算。我发现这是枚举的奇怪默认行为;虽然拥有严格枚举类型会很好,但是考虑到C ++通常与C兼容的愿望,我认为枚举类型的默认行为应该可以与整数互换。

其它参考11


定义


const int const_value = 5;


并不总是定义一个常量值。一些编译器(例如tcc 0.9.26)只是分配用名称const_value标识的内存。使用标识符const_value无法修改此内存。但你仍然可以使用另一个标识符修改内存:[73]


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


是定义不能通过任何方式修改的常量值的唯一方法。

其它参考12


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

其它参考13


虽然这个问题是关于整数的,但值得注意的是,如果你需要一个常量结构或字符串,#define和enums是无用的。这些都通常作为指针传递给函数。(使用字符串它是必需的;结构是它效率更高。)


对于整数,如果您处于内存非常有限的嵌入式环境中,您可能需要担心常量的存储位置以及如何编译对它的访问。编译器可能会在运行时添加两个consts,但添加两个#在编译时定义。#define常量可以转换为一个或多个MOV [[立即]]指令,这意味着常量有效地存储在程序存储器中。常量常量将存储在数据存储器的.const部分中。使用哈佛架构,可能会在性能和内存使用方面存在差异,尽管它们可能很小。它们可能对内环的硬核优化很重要。

其它参考14


一个简单的区别:


在预处理时,常量被其值替换。
因此,您无法将取消引用运算符应用于定义,但您可以将取消引用运算符应用于变量。


正如您所想的那样,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


我们查看了MBF16X上生成的汇编程序代码......两种变体都会产生相同的算术运算代码(例如ADD Immediate)。


所以const int是类型检查的首选,而#define是旧式。也许它是特定于编译器的。因此,请检查生成的汇编程序代码。