“静态const"与“# define"与“enum"

在C语言中,下列语句中使用哪个更好?

static const int var = 5;

#define var 5

enum { var = 5 };
382689 次浏览

一般来说:

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_),而仔细阅读库你会注意到这(通常)后面是特定区域(库)的名称,然后是一个有意义的名字。

它通常会产生很长的名字:)

不要认为“哪个总是最好的”有一个答案,但是,正如马蒂厄所说

# EYZ0

类型安全。我对#define最大的不满是,在Visual Studio中调试时,你不能看到变量。它给出一个无法找到符号的错误。

在c#中EYZ0更受欢迎。你可以使用这些值来声明数组大小,例如:

#define MAXLEN 5


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

据我所知,ANSI C不允许你在这种情况下使用static consts。在c++中,在这些情况下应该避免使用宏。你可以写

const int maxlen = 5;


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

甚至省略static,因为内部链接已经由const暗示[仅在c++中]。

在C语言中?在C语言中,正确的答案是:使用#define(或者,如果合适,enum)

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

所以,在C语言中,选择应该取决于你计划如何使用常数。例如,您不能使用const int对象作为case标签(而宏可以使用)。您不能使用const int对象作为位域宽度(而宏可以)。在C89/90中,你不能使用const对象来指定数组大小(而宏可以)。即使在C99中,当你需要一个非射电望远镜的数组时,你也不能使用const对象来指定数组大小。

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

在c++中,const对象是真正的常量,所以在c++中,最好使用const变体(但在c++中不需要显式使用static)。

如果可以使用它,static const有很多优点。它遵循正常的作用域原则,在调试器中可见,并且通常遵守变量所遵循的规则。

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

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

这取决于你需要这个值做什么。你(和目前所有人一样)忽略了第三种选择:

  1. # EYZ0
  2. # EYZ0
  3. # EYZ0

忽略名字的选择问题,那么:

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

因此,在大多数情况下,优先选择“enum”而不是其他选项。否则,第一个和最后一个要点很可能是控制因素——如果你需要同时满足这两个点,你必须更努力地思考。

如果你问的是c++,那么你每次都应该使用option (1) - static const。

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

如果是这样的话,丹尼斯·里奇会保留最好的一个…哈哈哈……: -)

顺便说一句,#define的另一个选择是“enum”,它提供了适当的作用域,但表现得像一个“真正的”常量。例如:

enum {number_ten = 10;}

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

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

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,否则…

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

例如,

struct mystruct {
int var;
};


#define var 5


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

预处理器将取代它,代码将无法编译。因此,传统的编码风格建议所有常量# eyz0使用大写字母以避免冲突。

的定义

const int const_value = 5;

并不总是定义一个常数值。一些编译器(例如太极拳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

是定义一个不能用任何方法修改的常数值的唯一方法。

一个简单的区别:

在预处理时,常量被替换为它的值。 因此,不能将解引用操作符应用于定义,但可以将解引用操作符应用于变量

如您所料,define比static 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的程序文本段的代码。

然而,对于static const,我们有一个被分配到某处的变量。对于gcc,静态const分配在程序的文本段中。

上面,我想讲的是引用操作符,所以用引用替换解引用。

使用const而不是#define总是更好。这是因为const是由编译器处理的,而#define是由预处理器处理的。这就像#define本身不是代码的一部分(粗略地说)。

例子:

#define PI 3.1416

符号名称PI可能永远不会被编译器看到;它可能在源代码到达编译器之前被预处理器删除。因此,名称PI可能无法进入符号表。如果在编译过程中遇到涉及使用常量的错误,这可能会令人困惑,因为错误消息可能指向3.1416,而不是PI。如果圆周率定义在一个你没有写的头文件中,你就不知道3.1416是从哪里来的。

这个问题也可能在符号调试器中突然出现,因为您正在编程的名称可能不在符号表中。

解决方案:

const double PI = 3.1416; //or static const...

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

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

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

#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给出警告时,enum给出一个错误。

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

至于整数,如果您处于内存非常有限的嵌入式环境中,您可能需要担心常量存储在哪里以及如何编译对它的访问。编译器可能在运行时添加两个const,但在编译时添加两个#定义。#define常量可以转换为一个或多个MOV [immediate]指令,这意味着常量有效地存储在程序内存中。const常量将存储在数据内存中的.const节中。在采用Harvard体系结构的系统中,性能和内存使用可能存在差异,尽管差异可能很小。它们可能对内部循环的核心优化很重要。

我不确定我是否正确,但在我看来,调用#defined值比调用任何其他正常声明的变量(或const值)快得多。 这是因为当程序运行时,它需要使用一些正常声明的变量,它需要跳转到内存中的确切位置来获得该变量

相反,当它使用#defined值时,程序不需要跳转到任何已分配的内存,它只是接受该值。如果程序调用#define myValue 7myValue,它的行为与只调用7时完全相同。