在下面的c语句中,哪一个更适合使用?
1
| static const int var = 5; |
或
或
- 有趣的是,这个问题与stackoverflow.com/questions/1637332/static-const-vs-define几乎完全相同。唯一的区别是,这个问题是关于C++的,这个问题是关于C的,因为我的答案是C++特定的,我说这使它们不相同,但是其他人可能不同意。
- 不完全相同。由于兼容性的原因,C++有很多地方允许C语法。在这些情况下,诸如"什么是X的最佳方式"之类的问题在C++中会有不同的答案。例如,对象初始化。
- 另外:stackoverflow.com/questions/1944041/…
- 这怎么不是基于意见的?他们每个人都有不同的目的
这取决于你需要的价值。你(以及到目前为止的所有人)忽略了第三种选择:
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。
- 太棒了!enum的一个缺点是,它们作为int实施(〔c99〕6.7.2.2/3)。#define允许您使用U和L后缀指定unsigned和long,const允许您提供一个类型。enum会导致常规类型转换出现问题。
- (2)人们总是抱怨类型安全。我永远不明白为什么不使用"define var((int)5)"和hurray,您使用define获得了类型安全性。
- 除了@gauthier所说的以外,备选方案(1)和(2)也可以用于非整数常量。
- 空间人为环境中的内存使用情况如何?只有#define才能保证不消耗任何额外的空间吗?
- @雷德克斯:你必须在一个非常特殊的环境中,才能让空间受到关注。也就是说,enum和#define本身都不使用额外的空间。该值将作为指令的一部分出现在对象代码中,而不是在数据段、堆或堆栈中分配存储。您将为static const int分配一些空间,但如果不取地址,编译器可能会对其进行优化。
- 你一直在研究阿杜诺…非常小的内存空间(小于2 K)…选项2有很大帮助。
- @保罗:枚举所占用的空间不会超过定义的空间,请参阅gcc中的"fshort-enums"。
- @谢谢你的反馈:既然你在ARM处理器里…;-)您知道这是否也适用于Arduino AVR GCC编译器?
- @保罗:我不是专家,但我会说是的,fshort-enums选项也适用于avr-gcc;-)
- 枚举还可以像这样启用初始化:enum { size=10 } ; char str[size] = {0};。
- 这是一enums(for投票和static const):我不能改变他们。在definecan be #undefineD是固定在一enum和static constgiven to the value。
- 另一个使用的摇篮#defineof when to embed最少的人想要一个专业和软件版本号码(since they often于变化不甚相关的头文件中的值)在他们的源代码。
- are there any where是16位compilers静电const uint32_t var = 0xFFFF0000;result=0x0000无功?做什么和说c99 about the size of C89规格在不断?
- for the Mind把你圣爱# parentheses定义值
- 真实的:"不,谢谢你。不幸的是,外面的parentheses简单安全。现在,告诉我,如何程序可以编译legitimately expected to be not the蚀变模式会很好parentheses 5。if it was the argument to a函数式宏或if there any,是经营者在the expression,那么你是正确的我有责任parentheses not included the。但我不是在这里的茶馆。
- 我知道安全的替代最小二乘型(3)好。在这3例是移调的example,the to have型模式和标准int好sizeof(int)字节长。一类型的变量,不管一个人多,可以是任何类型,包括char,unsigned intsigned int布尔。如果你使用枚举常数,它是尝试使用枚举变量也甚to,but because of the C在愚蠢的错误在设计标准(6.7.2.2),这些可能not have the same type!例中,where the size of the枚举变量校正变量是avoided because they should be,T型安全。
- 一disadvantage of using that they is静态const定义的常量的行话be used to other values。EG:static const a = 5; static const b = a; //warning: use of const variable in a constant expression is non-standard in C
- 我constinstead of using constexprabout?
- danm:@ C。(which is the question of using this话题),将是在constexprsyntax error。在C + +,你可以(和适当的constexprprobably should)时使用。我不确定我能来确定的succinctly constexprshould be used when to const在偏好,虽然。
- danm @。这似乎是在良好的堆栈溢出Q &;布尔差分constbetween constexprabout?
- 在进口端(3)(2)快速,而不定义为复合。自定义页眉,在redefines增(2)和(3)与绑定的端口号. helps creation of。
- 茶与枚举的问题是,他们不提供C型安全参数指定当passed as to a函数或变量。C编译器,不会产生感知of AM警告如果你在错误的类型使用。
- not sure意思你@汽车轴承。在非古versions of -Wenum-conversion(GCC /铿锵,which may be with -Wall默认使能模式或呼叫超时当枚举)将在assignments布尔函数都是在混合。
一般来说:
因为它尊重范围并且是类型安全的。
唯一需要注意的是:如果您希望在命令行上定义变量的话。还有一种选择:
1 2 3 4 5
| #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),在阅读库时,您会注意到这(通常)后面跟着部件名称。特殊区域(图书馆),然后用一个有意义的名字。
它通常有很长的名字:)
- 同意-也同意定义,由于预处理器不知道语法,因此存在损坏代码的一般危险。
- 使用if比ifdef更好,但我同意。+ 1。
- 请参阅:stackoverflow.com/questions/135069/…了解差异。实际上,CCAN已经将其作为其风格指南(Rusty的宠物皮之一)的一部分来实施。
- 这是标准的C++福音书。下面的答案更清楚地解释了这些选项的真正含义。特别是:我刚遇到一个"静态常量"的问题。有人用它在头文件中定义了大约2000个"常量"。然后这个头文件包含在大约100个".c"和".cpp"文件中。对于"consts",大于8兆字节。伟大的。是的,我知道您可以使用链接器删除未引用的常量,但这样仍然会留下引用的"consts"。空间不够,这个答案怎么了?
- @英戈布莱克曼:有了一个好的编译程序,只有那些地址被取下的static;如果地址被取下,就不能使用#define或enum(没有地址)。所以我真的不知道该用什么替代方法。如果您可以取消"编译时评估",那么您可能会寻找extern const。
- @Tim Post:#if对于布尔标志可能比#ifdef更可取,但在这种情况下,无法从命令行中将var定义为0。因此,在这种情况下,只要0是var的法定价值,#ifdef就更有意义了。
- @Matthieu:我在找"define const_value((int)5)"。如果我想要一个常量值,如果我不能取"const_value"的地址,这是一个优势。如果我想要简单的整型常量值,我不想要它们的地址。
- 顺便问一句,static constexpr怎么样?它不应该像静态常量那样没有开销吗?
- @danm.:对于整数,应该没有区别。constexpr与const是关于评估值的时间。constexpr保证在编译时对该值进行评估(因此在main启动之前不执行任何函数),这也保证了优化程序可以使用该值…但是请注意,这是一个C问题,C中没有constexpr。
- 这是一个C问题,而不是C++。c上的const表示只读,不是常量。可以在块内声明常量,获取指向该常量的指针并修改其值。请参阅此示例
- 哦,当时我回答说,它被标记为C++(现在有相当一段时间了,嗯?).另外,使用gcc -std=c89 -pedantic编译生成warning: initialization discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers],使用-O2打印"const really means constant",因此我担心您只是一个许可编译器的受害者。
- 注意,在C99内联函数中,不能引用静态变量,因此这是一个场景,您必须仍然使用define的。
- 删除静态,使用匿名命名空间。命名空间const int var=123;
- @ Musky:C中没有命名空间;在C++中EDCOX1(0)是完全可以接受的。
尤其是C语言?在c中,正确答案是:使用#define(或者,如果合适,使用enum)
虽然有一个EDCOX1×7对象的范围和打字特性是有益的,但是在现实中EDCOX1中,C(相对于C++)中的7个对象不是真的常数,因此在大多数实际情况下通常是无用的。
所以,在C语言中,选择应该由你打算如何使用常数来决定。例如,不能将const int对象用作case标签(而宏可以工作)。不能将const int对象用作位字段宽度(而宏可以工作)。在C89/90中,不能使用const对象来指定数组大小(而宏可以工作)。即使在c99中,当需要非VLA数组时,也不能使用const对象来指定数组大小。
如果这对你很重要,那么它将决定你的选择。大多数时候,除了在C语言中使用#define之外,你别无选择,也别忘了另一种方法,它在C语言中产生真正的常量。
在C++ EDCOX1中,7个对象是真正的常量,所以在C++中,最好总是选择EDCOX1×7的变体(不需要在C++中使用显式EDCOX1 6)。
- "不能将const in t对象用作case标签(宏可以工作时)"——>关于此语句,我在switch中的c中测试了const变量,它正在工作….
- @约翰:嗯,你需要提供你测试的代码,并命名特定的编译器。使用const int对象以防标签在所有C语言版本中都是非法的。(当然,编译器可以自由地支持它作为非标准C++语言扩展。)
- "……"因此,在大多数实际情况下都是无用的。"我不同意。只要不需要将名称用作常量表达式,它们就非常有用。C中的"常量"一词表示可以在编译时计算的内容;const表示只读。const int r = rand();是完全合法的。
- 在C++中,最好使用EDCOX1×4×EDCX1,2,特别是EDCOX1,6个容器,如EDCOX1,7,EDCX1,8。
- @约翰,你一定是在switch()声明中测试过,不是在case声明中。我也被这件事缠住了?
static const和#define的区别在于前者使用内存,后者不使用内存进行存储。其次,您不能传递#define的地址,而可以传递static const的地址。实际上,这取决于我们所处的环境,我们需要在这两种情况中选择一种。在不同的情况下,两者都处于最佳状态。请不要以为一个比另一个好…-)
如果是这样的话,丹尼斯·里奇会把最好的一个单独留在这里……哈哈哈…-)
- for some提+ 1存储,嵌入式系统还不多,虽然有可能会启动,关闭和使用静态常量只改变# defines if needed。
- 我只是测试它。真的,uses const int或额外的存储compared to #定义枚举。嵌入式系统,因为我们的计划,我们不能afford the additional内存的使用。我知道,我们回到#枚举定义或使用。
- practically en is not true(说了),constdoes使用存储。海湾合作委员会(测试和一些newer versions with 4.5.3)const intinto the easily optimizes直接literal when using-在你的尾巴。如果你给我的低内存(例如嵌入式开发AVR)你可以安全使用,如果你使用GCC C常量或一个兼容的编译器。但我不期望它给你测试的铿锵to the same thing BTW。
在C.#define中更受欢迎。可以使用这些值声明数组大小,例如:
1 2 3 4 5
| #define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
} |
据我所知,ansi c不允许在这种情况下使用static const。在C++中,在这些情况下应该避免宏。你可以写
1 2 3 4 5
| const int maxlen = 5;
void foo() {
int bar[maxlen];
} |
甚至省略EDOCX1,6,因为内部链接是由EDOCX1,7(已经在C++中)所暗示的。
- "内部联系"是什么意思?我可以在一个文件中使用const int MY_CONSTANT = 5;,在另一个文件中使用extern const int MY_CONSTANT;访问它。我在标准(至少c99)中找不到关于const更改默认行为"6.2.2:5如果声明一个标识?呃,对于一个物体来说?不指定存储类?呃,它的连接是外部的"。
- @高蒂埃:对不起,关于这个。我应该说"已经被C++语言中的const所暗示"。这是特定于C++的。
- @Sellibitze,很高兴在这一过程中看到一些争论,而不是大量的意见如果真的争论会有额外的好处,你明白了!
- 从c99开始,您的第二个代码片段是合法的。bar是一个VLA(可变长度数组);编译器很可能生成代码,就好像其长度是常量一样。
c中const的另一个缺点是不能在初始化另一个const时使用该值。
1 2 3 4 5 6
| 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; |
即使这样也不适用于常量,因为编译器不将其视为常量:
1 2 3
| 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,否则…
- 比赛有点晚了,但这个问题又出现在另一个问题上。追寻为什么你的static uint8_t const ARRAY_SIZE = 16;突然不再编译可能有点挑战性,特别是当#define ARRAY_SIZE 256被深埋在十层纠结的报头网络中时。所有冠名为ARRAY_SIZE的人都在自找麻烦。保留宏的所有大写字母,不要定义不是所有大写字母形式的宏。
- @大卫:合理的建议,我会遵照执行。
- 4年后,你为我节省了很多时间来思考为什么我不能"筑巢"const。这可能会被否决更多!
如果你能摆脱它,static const有很多优势。它遵循正常的作用域原则,在调试器中可见,并且通常遵循变量遵守的规则。
但是,至少在最初的C标准中,它不是一个常数。如果使用#define var 5,则可以编写int foo[var];作为声明,但不能这样做(除了作为static const int var = 5;的编译器扩展)。这不是C++中的情况,EDCOX1的2版本可以在EDCOX1×3版本的任何地方使用,我相信C99也是如此。
但是,不要用小写名称命名#define常量。它将覆盖该名称的任何可能使用,直到翻译单元结束。宏常量应该位于它们自己的有效名称空间中,该名称空间传统上都是大写字母,可能带有前缀。
- 不幸的是,C99并非如此。c99中的const仍然不是一个真正的常量。可以使用c99中的const声明数组大小,但这只是因为c99支持可变长度数组。因此,它只在允许VLA的情况下工作。例如,即使在c99中,也不能使用const来声明struct中成员数组的大小。
- 虽然C99不会让你这样做是正确的,但是GCC(4.5.3测试)将完美地让你用EDCOX1×1的大小初始化数组,就好像它是C++ const或宏一样。无论您是否希望依赖GCC与标准之间的这种偏差,当然是您的选择,我个人都会使用它,除非您真的可以放弃使用其他编译器而不是GCC或Clang,后者在这里具有相同的特性(使用Clang3.7测试)。
我编写了快速测试程序来演示一个区别:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #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
", DEFINED_DEFINED , ENUM_DEFINED );
return(0);
} |
这将编译以下错误和警告:
1 2 3 4 5 6 7 8 9 10 11 12
| 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给出警告时,枚举给出错误。
如果你有像mystruct.var这样的东西,#define var 5会给你带来麻烦。
例如,
1 2 3 4 5 6 7 8 9 10 11
| struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
} |
预处理器将替换它,代码将无法编译。因此,传统的编码方式建议所有常量#define使用大写字母来避免冲突。
最好使用const,而不是定义。这是因为const由编译器处理,并由预处理器定义。这就像定义自己不是代码的一部分(粗略地说)。
例子:
编译器可能永远看不到符号名pi;它可能在源代码到达编译器之前被预处理器删除。因此,名称pi可能无法输入到符号表中。如果在编译过程中遇到涉及常量使用的错误,这可能会令人困惑,因为错误消息可能引用3.1416,而不是pi。如果pi是在一个没有写入的头文件中定义的,那么就不知道3.1416是从哪里来的。
这个问题也可能出现在符号调试器中,因为您正在编程的名称可能不在符号表中。
解决方案:
1
| const double PI = 3.1416; //or static const... |
定义
1
| const int const_value = 5; |
不总是定义常量值。一些编译器(例如TCC 0.9.26)只分配名为"const_value"的内存。使用标识符"常量值"不能修改这个内存。但是您仍然可以使用另一个标识符修改内存:
1 2 3 4
| 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。
- 你说得对。我用Visual Studio 2012测试了代码,它打印了5。但不能修改#define,因为它是一个预处理器宏。它在二进制程序中不存在。如果要修改所有使用CONST_VALUE的地方,就必须逐个修改。
- @Ugoren:假设您编写#define CONST 5,然后编写if (CONST == 5) { do_this(); } else { do_that(); },编译器将删除else分支。您建议如何编辑机器代码以将const更改为6?
- @基思汤普森,我从来没有说过这件事可以轻而易举、可靠地完成。只是#define不是防弹的。
- @乌戈伦:我的观点是,"编辑机器代码"并不是复制改变#define值的效果的明智方法。唯一真正的方法是编辑源代码并重新编译。
不要以为"哪一个总是最好的"有答案,但正如马修所说
static const
类型安全。不过,我对#define最大的不满是,在Visual Studio中调试时,您无法观看变量。它给出一个错误,即找不到符号。
- "你不能看变量"对,它不是变量。它不会改变,你为什么要看它?只需搜索标签,就可以找到使用它的所有地方。为什么你需要(甚至想要)看一个定义?
顺便说一句,#define的一个替代方法是"枚举",它提供了适当的范围,但其行为类似于"真正的"常量。例如:
在许多情况下,定义枚举类型并创建这些类型的变量是很有用的;如果这样做了,调试器可能能够根据其枚举名称显示变量。
然而,一个重要的警告是:在C++中,枚举类型与整数的兼容性有限。例如,默认情况下,不能对它们执行算术运算。我发现这是对枚举的一种奇怪的默认行为;虽然有一个"严格枚举"类型是好的,但是由于希望C++与C兼容,所以我认为"EnUM"类型的默认行为应该与整数互换。
- 在C语言中,枚举常量的类型始终是int类型,因此"enum hack"不能与其他整数类型一起使用。(枚举类型与某些实现定义的整数类型兼容,不一定是int,但在这种情况下,该类型是匿名的,因此这并不重要。)
- @基思汤普森:自从我写了以上内容,我已经读到,如果编译器将除int以外的类型分配给枚举类型变量(编译器可以这样做),并且试图将自己的枚举成员分配给此类变量,misra-c将发出尖叫声。我希望标准委员会可以添加用指定语义声明整数类型的可移植方法。任何平台,不管char大小,都应该能够声明一个将包装mod 65536的类型,即使编译器必须添加大量AND R0,#0xFFFF或等效指令。
- 您可以使用uint16_t,当然这不是枚举类型。让用户指定用于表示给定枚举类型的整数类型是很好的,但是您可以使用typedef表示uint16_t,使用一系列#define表示单个值来实现大致相同的效果。
- @基思汤普森:我理解,出于历史原因,我们坚持这样一个事实:一些平台将评估2U < -1L为真,而其他平台将评估为假,我们现在坚持这样一个事实:一些平台将在uint32_t和int32_t之间执行签名和未签名的比较,但这并不意味着ITTE无法定义一个向上兼容的C继承者,该继承者包含的类型的语义在所有编译器上都是一致的。
尽管这个问题是关于整数的,但值得注意的是,如果需要一个常量结构或字符串,define和enum是无用的。它们通常都作为指针传递给函数。(对于字符串,这是必需的;对于结构,它效率更高。)
对于整数,如果您处于内存非常有限的嵌入式环境中,可能需要担心常量存储在哪里以及如何编译对它的访问。编译器可能在运行时添加两个常量,但在编译时添加两个定义。定义常量可以转换成一个或多个MOV[立即]指令,这意味着常量有效地存储在程序内存中。常量将存储在数据内存的.const节中。在具有哈佛体系结构的系统中,性能和内存使用可能存在差异,尽管它们可能很小。它们对于内部循环的核心优化可能很重要。
一个简单的区别:
在预处理时,常量被其值替换。因此,不能将取消引用运算符应用于定义,但可以将取消引用运算符应用于变量。
如您所想,define比static const更快。
例如,具有:
你不能做printf("address of constant is %p",&mymax);。
但拥有
你可以做printf("address of constant is %p",&mymax_var);。
更清楚地说,在预处理阶段,定义被它的值替换,所以我们在程序中没有存储任何变量。我们只有程序文本段中使用定义的代码。
但是,对于静态常量,我们有一个变量被分配到某个地方。对于gcc,静态常量分配在程序的文本段中。
上面,我想介绍一下引用操作符,所以用引用替换取消引用。
- 你的回答是错的。这是关于C的,你的答案涉及C++,它对于EDCOX1的15度限定符有非常不同的语义。C没有枚举常量以外的符号常量。const int是一个变量。您还混淆了语言和特定的实现。无需将物体放在何处。对于GCC来说,这甚至不是真的:它通常将const限定变量放在.rodata部分中。但这取决于目标平台。你是说EDOCX1号(30号)接线员的地址。
我不确定自己是否正确,但在我看来,调用#defined值比调用任何其他通常声明的变量(或常量值)快得多。这是因为当程序运行并且需要使用一些通常声明的变量时,它需要跳转到内存中的确切位置来获取该变量。
相反,当它使用#defined值时,程序不需要跳转到任何已分配的内存,它只需要获取该值。如果#define myValue 7和调用myValue的程序的行为与它刚才调用7时完全相同。
我们研究了MBF16X上生成的汇编程序代码…两种变体都会产生相同的算术运算代码(例如,添加immediate)。
因此,类型检查首选const int,而#define是旧样式。也许它是特定于编译器的。因此,请检查生成的汇编程序代码。