在下面的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楼
这取决于您需要的价值。 您(以及到目前为止的其他所有人)省略了第三种选择:
-
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。
#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
时完全相同。