在下面的C语言语句中,哪个更好使用?
static const int var = 5;
要么
#define var 5
要么
enum { var = 5 };
在下面的C语言语句中,哪个更好使用?
static const int var = 5;
要么
#define var 5
要么
enum { var = 5 };
Answers:
这取决于您需要的价值。您(以及到目前为止的其他所有人)省略了第三种选择:
static const int var = 5;
#define var 5
enum { var = 5 };
忽略有关名称选择的问题,然后:
因此,在大多数情况下,更喜欢“枚举”而不是其他选择。否则,第一个和最后一个要点可能是控制因素-如果需要同时满足这两个条件,则必须加倍考虑。
如果您询问的是C ++,那么您每次都会使用选项(1)-静态const。
enum
是它们被实现为int
([C99] 6.7.2.2/3)。A #define
可让您指定unsigned和long与U
和L
后缀,并const
指定类型。enum
可能会导致通常的类型转换出现问题。
enum
也不#define
使用额外的空间。该值将作为指令的一部分出现在目标代码中,而不是被分配存储在数据段,堆或堆栈中。您将为分配一些空间static const int
,但是如果您不占用地址,编译器可能会对其进行优化。
enum
s(和static const
)的另一个“投票” :它们不能更改。一个define
可以是#undefine
“d其中一个enum
和static const
被固定到给定的值。
一般来说:
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_ ),在仔细阅读库时,您会注意到,(通常)后面是特定区域(库)的名称,然后是有意义的名称。
通常,它使用冗长的名称:)
static
地址被占用的那些才应该保留;如果采用该地址,则可能无法使用“ #define
或” enum
(无地址)...,所以我真的看不到可以使用哪种替代方法。如果您可以取消“编译时间评估”,那么您可能正在寻找extern const
。
#if
可能更可取#ifdef
,但是在这种情况下,将无法从命令行定义var
as 0
。因此,在这种情况下,#ifdef
只要0
的合法价值就更有意义var
。
特别是在C中?在C语言中,正确的答案是:使用#define
(或在适当情况下,enum
)
虽然具有对象的作用域和键入属性是有益的,但const
实际上const
C对象(与C ++相反)不是真正的常数,因此在大多数实际情况下通常是无用的。
因此,在C语言中,选择应取决于计划使用常量的方式。例如,您不能将const int
对象用作case
标签(而宏将起作用)。您不能将const int
对象用作位域宽度(而宏将起作用)。在C89 / 90中,您不能使用const
对象指定数组大小(而宏将起作用)。即使在C99中,const
当您需要非VLA时也无法使用对象指定数组大小数组。
如果这对您很重要,那么它将决定您的选择。大多数时候,您别无选择,只能#define
在C中使用。别忘了另一种选择,它在C-中产生真常数enum
。
在C ++中,const
对象是真正的常量,因此在C ++中,通常总是更喜欢const
变体(无需显式)。static
尽管C ++中)。
const int
在所有版本的C语言中,在大小写标签中使用对象都是非法的。(当然,您的编译器可以自由地将其作为非标准的C ++语言扩展来支持。)
const
表示只读。const int r = rand();
是完全合法的。
constexpr
与const
专门用于或的stl
容器相比,最好使用。array
bitset
switch()
语句中测试过,而不是case
一个。我也刚好被这一个抓住了
static const
和之间的区别在于,#define
前者使用内存,而后者不使用内存进行存储。其次,您不能传递的地址,#define
而可以传递的地址static const
。实际上,这取决于我们所处的环境,我们需要在这两种情况中选择一种。两者在不同情况下都处于最佳状态。请不要以为一个优于另一个... :-)
const
使用内存确实是(不再)事实。const int
使用-O3时,GCC(已在4.5.3和一些较新版本中进行了测试)可轻松地将代码优化为直接文字。因此,如果您进行低RAM嵌入式开发(例如AVR),并且使用GCC或其他兼容的编译器,则可以安全地使用C const。我还没有测试过,但是希望Clang可以做同样的事情。
在C #define
中更受欢迎。您可以使用这些值来声明数组大小,例如:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
static const
据我所知,ANSI C不允许您在此上下文中使用。在C ++中,在这种情况下应避免使用宏。你可以写
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
甚至省去了,static
因为const
已经暗示了内部链接(仅在C ++中)。
const int MY_CONSTANT = 5;
在一个文件extern const int MY_CONSTANT;
中使用另一个文件。我在标准(至少C99)中找不到有关const
更改默认行为的任何信息“ 6.2.2:5如果对象的标识符声明具有文件范围且没有存储类指定者,则其链接是外部的”。
bar
是VLA(可变长度数组);编译器可能会生成代码,就好像其长度是恒定的一样。
const
C语言的另一个缺点是您不能在初始化另一个值时使用该值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
在这些情况下,我很乐意使用typed ,否则...
static uint8_t const ARRAY_SIZE = 16;
突然不再编译的过程可能会有些挑战,特别是当将#define ARRAY_SIZE 256
其埋在纠缠的标头网中十层深的地方时。所有大写字母的名字ARRAY_SIZE
都在麻烦。为宏保留ALL_CAPS,不要定义非ALL_CAPS形式的宏。
const
。这可能需要更多支持!
如果可以摆脱它,static const
则具有很多优势。它遵循正常的作用域原则,在调试器中可见,并且通常遵循变量遵循的规则。
但是,至少在最初的C标准中,它实际上不是常数。如果使用#define var 5
,则可以将其编写int foo[var];
为声明,但不能这样做(除非作为编译器扩展名,否则使用static const int var = 5;
。)在C ++中不是这种情况,因为该static const
版本可以在该版本可以使用的任何地方使用#define
,我相信C99也是如此。
但是,切勿#define
使用小写字母命名常量。在翻译单元末尾之前,它将覆盖该名称的所有可能用法。宏常量应该有效地位于它们自己的名称空间中,该名称空间传统上是所有大写字母,可能带有前缀。
const
在C99中,仍然不是一个真正的常数。您可以const
在C99 中用声明数组大小,但这仅是因为C99支持可变长度数组。因此,它仅在允许VLA的地方起作用。例如,即使在C99中,您仍然无法使用const
来在中声明成员数组的大小struct
。
const int
大小与C ++ const或宏一样的数组。是否要依赖GCC与标准的这种偏离当然是您的选择,除非您能真正预见使用GCC或Clang以外的其他编译器,否则我会亲自使用它,后者在此处具有相同的功能(已通过Clang测试) 3.7)。
始终最好使用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...
我编写了快速测试程序来演示一个区别:
#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给出错误。
定义
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
是定义不能以任何方式修改的常数值的唯一方法。
#define
也可以通过编辑机器代码进行修改。
5
。但是一个人不能修改,#define
因为它是一个预处理器宏。它在二进制程序中不存在。如果要修改所有CONST_VALUE
使用过的地方,则必须一一修改。
#define CONST 5
,然后编写,然后if (CONST == 5) { do_this(); } else { do_that(); }
编译器将删除该else
分支。您如何建议将机器代码编辑CONST
为6?
#define
不是防弹的。
#define
。唯一的真正方法是编辑源代码并重新编译。
尽管问题是关于整数的,但值得注意的是,如果需要恒定的结构或字符串,则#define和enum是无用的。这些通常都作为指针传递给函数。(使用字符串是必需的;使用结构则效率更高。)
对于整数,如果您处于内存非常有限的嵌入式环境中,则可能需要担心常量的存储位置以及对常量的访问方式。编译器可能会在运行时添加两个const,但在编译时添加两个#define。可以将#define常数转换为一个或多个MOV [立即]指令,这意味着该常数有效地存储在程序存储器中。常量将存储在数据存储器的.const节中。在具有哈佛体系结构的系统中,性能和内存使用量可能会有所不同,尽管它们可能很小。它们对于内部循环的硬核优化可能很重要。
不要以为“永远是最好的”是有答案的,但是正如Matthieu所说
static const
是类型安全的。#define
不过,我最大的烦恼是,在Visual Studio中进行调试时,您无法监视该变量。出现错误,找不到该符号。
顺便说一下,#define
提供了适当作用域但其行为类似于“真实”常量的替代方法是“枚举”。例如:
enum {number_ten = 10;}
在许多情况下,定义枚举类型并创建这些类型的变量很有用;如果这样做,调试器可能能够根据其枚举名称显示变量。
但是,这样做的一个重要警告是:在C ++中,枚举类型与整数的兼容性有限。例如,默认情况下,不能对它们执行算术运算。我发现这对于枚举是一种奇怪的默认行为。尽管有一个“严格的枚举”类型会很好,但考虑到C ++通常与C兼容的愿望,我认为“枚举”类型的默认行为应该可以与整数互换。
int
,因此“ enum hack”不能与其他整数类型一起使用。(枚举类型不一定与某些实现定义的整数类型兼容int
,但在这种情况下,该类型是匿名的,因此无关紧要。)
int
给枚举类型的变量以外的类型(允许编译器执行),并且试图将其分配给这样的变量,则MISRA-C会发出嘶哑的声音自己枚举的成员。我希望标准委员会会添加一些可移植的方法来声明具有指定语义的整数类型。 任何平台,无论char
大小,都应该能够声明一个将包装mod 65536的类型,即使编译器必须添加很多AND R0,#0xFFFF
或等效的指令也是如此。
uint16_t
,尽管这当然不是枚举类型。最好让用户指定用于表示给定枚举类型的整数类型,但是对于单个值,使用typedef
for uint16_t
和一系列#define
s 可以达到很多相同的效果。
2U < -1L
为true,而另一些平台的评估结果为false,而我们现在仍然坚持这样的事实,即某些平台将实现uint32_t
和int32_t
签名之间的比较还有一些是未签名的,但这并不意味着委员会无法定义C的向上兼容的后继程序,该后继程序包括其语义在所有编译器上都一致的类型。
一个简单的区别:
在预处理时,常数将替换为其值。因此,您不能将取消引用运算符应用于定义,但是可以将取消引用运算符应用于变量。
如您所愿,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分配在程序的文本段中。
上面,我想介绍一下引用运算符,所以用引用替换取消引用。
const
限定词语义非常不同。C除了枚举常量之外没有符号常量。A const int
是变量。您还会混淆语言和特定的实现。无需放置对象。对于gcc甚至都不是这样:通常,它将const
合格的变量放在该.rodata
部分中。但这取决于目标平台。您的意思是操作员的地址&
。