“静态常量”与“ #define”与“枚举”

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

static const int var = 5;

要么

#define var 5

要么

enum { var = 5 };

#1楼

如果您具有诸如mystruct.var类的内容, mystruct.var #define var 5将会给您带来麻烦。

例如,

struct mystruct {
    int var;
};

#define var 5

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

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


#2楼

一般来说:

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_),在细读库时,您会注意到(通常)后跟特定区域(库)的名称,然后加上有意义的名称。

通常,它使用冗长的名称:)


#3楼

不要以为“永远是最好的”是有答案的,但是正如Matthieu所说

static const

是类型安全的。 但是,我最大的#define烦恼是,在Visual Studio中进行调试时,您无法监视该变量。 出现错误,找不到该符号。


#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中? 在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 )。


#6楼

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

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

但是,切勿使用小写字母命名#define常数。 在翻译单元末尾之前,它将覆盖该名称的所有可能用法。 宏常量应该有效地位于它们自己的名称空间中,该名称空间传统上是所有大写字母,可能带有前缀。


#7楼

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

  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。


#8楼

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

如果真是那样的话, 丹尼斯·里奇本该保持最好的一个……哈哈哈……:-)


#9楼

定义

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

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


#10楼

一个简单的区别:

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

如您所愿,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);

更清楚地说,在预处理阶段,定义将被其值替换,因此程序中没有存储任何变量。 我们只有使用定义的程序文本段中的代码。

但是,对于静态const,我们有一个分配在某处的变量。 对于gcc,静态const分配在程序的文本段中。

上面,我想介绍一下引用运算符,所以用引用替换取消引用。


#11楼

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

#12楼

我们在MBF16X上查看了产生的汇编代码。这两种变体在算术运算中都产生相同的代码(例如,ADD Instant)。

因此,在#define为旧样式时,最好使用const int作为类型检查。 也许是特定于编译器的。 因此,请检查您生成的汇编代码。


#13楼

我编写了快速测试程序来演示一个区别:

#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给出错误。


#14楼

顺便提一下,# #define的替代方法是“枚举”,该替代方法提供适当的作用域,但其行为类似于“真实”常量。 例如:

enum {number_ten = 10;}

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

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


#15楼

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


#16楼

尽管问题是关于整数的,但值得注意的是,如果需要恒定的结构或字符串,则#define和enum是无用的。 这些通常都作为指针传递给函数。 (使用字符串是必需的;使用结构则效率更高。)

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


#17楼

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

相反,当它使用#define d值时,程序不需要跳转到任何分配的内存,它只需要使用该值即可。 如果#define myValue 7和调用myValue的程序,则其行为与仅调用7时完全相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值