为啥大多数 C 开发人员使用 define 而不是 const? [复制]
Posted
技术标签:
【中文标题】为啥大多数 C 开发人员使用 define 而不是 const? [复制]【英文标题】:Why do most C developers use define instead of const? [duplicate]为什么大多数 C 开发人员使用 define 而不是 const? [复制] 【发布时间】:2011-04-30 18:33:50 【问题描述】:在许多程序中,#define
的作用与常量相同。例如。
#define FIELD_WIDTH 10
const int fieldWidth = 10;
我通常认为第一种形式优于另一种形式,它依赖于预处理器来处理基本上是应用程序决策的事情。这种传统有原因吗?
【问题讨论】:
请注意,对于整数常量,您还可以使用像 ``enum FIELD_WIDTH = 10;'' 中的枚举,如果您需要函数范围内的编译时常量,这将很有用。 你也可以在函数范围内#define,尽管你必须记住在函数结束时#undef它 @CashCow:没错,但是您在文件范围内取消定义可能的#define(可能已在您自己未编写的包含文件中定义)。 @Jochen:当与另一个宏定义发生冲突时,您的 #define 已经覆盖了之前的定义并且您忽略了编译器警告,因此您也可以 #undef 它并生成在代码中进一步使用宏时出现编译器错误,提醒您应该更改函数范围宏的宏名称。 @Secure:当然,但是你必须处理名称冲突。局部作用域的优点是可以避免它。当然,使用本地枚举也不是万无一失的:如果全局 #define 将 FIELD_WIDTH 重写为其他值,例如 20,则枚举将被重写为`enum 20 = 10;`
如果 FIELD_WIDTH 的全局和本地定义都是枚举,但,这两种定义确实共存(局部定义遮蔽了全局定义)。 (有关此主题的更多信息,请参阅eetimes.com/discussion/programming-pointers/4023879/…。
【参考方案1】:
这有一个非常充分的理由:C 中的const
并不意味着某些东西是恒定的。这只是意味着变量是只读的。
在编译器需要真正常量的地方(例如对于非 VLA 数组的数组大小),使用 const
变量,例如 fieldWidth
是不可能的。
【讨论】:
+1 来自不懂 C 的 C++ 编码人员的大量错误答案中的正确答案。 @C.罗斯:一致性。所有清单常量通常用#define
s 定义。 const
仅用于指示只读(a 的访问路径)变量。我知道,C 语言中的const
完全坏了:-)
你不能说 const int size 4;然后再做 char str[size]?
@fahad:不在 C89 中。在 C99 中,您可以为自动存储持续时间的变量这样做,但从技术上讲,它是一个 VLA。
@Exception:C 和 C++ 在处理 const
方面是非常不同的语言。在 C++ 中,const
可用于正确的常量,而在 C 中则不能。【参考方案2】:
它们是不同的。
const
只是一个限定符,它表示变量不能在运行时更改。但是该变量的所有其他特性仍然存在:它已分配存储空间,并且该存储空间可能会被寻址。所以代码不只是把它当作一个字面量,而是通过访问指定的内存位置来引用变量(除非是static const
,那么它可以被优化掉),并在运行时加载它的值。由于const
变量已分配存储空间,如果将其添加到标头并将其包含在多个C 源代码中,除非将其标记为extern
,否则会出现“多符号定义”链接错误。在这种情况下,编译器无法根据其实际值优化代码(除非启用全局优化)。
#define
只是将名称替换为其值。此外,#define
'd 常量可以在预处理器中使用:您可以将其与#ifdef
一起使用,根据其值进行条件编译,或使用字符串化运算符#
来获取带有其值的字符串。并且由于编译器在编译时知道它的值,它可以根据该值优化代码。
例如:
#define SCALE 1
...
scaled_x = x * SCALE;
当SCALE
定义为1
时,编译器可以消除乘法,因为它知道x * 1 == x
,但如果SCALE
是(extern
)const
,则需要生成代码获取值并执行乘法,因为直到链接阶段才知道该值。 (使用多个源文件中的常量需要extern
。)
使用枚举更接近于使用#define
:
enum dummy_enum
constant_value = 10010
;
但这仅限于整数值,没有#define
的优点,所以没有被广泛使用。
const
在您需要从某个编译它的库中导入常量值时很有用。或者如果它与指针一起使用。或者,如果它是通过变量索引值访问的常量值数组。否则,const
与#define
相比没有优势。
【讨论】:
+1 用于提及enum
作为替代 - 这应该更频繁地使用。一个主要优点是该符号可供调试器使用。
我宁愿认为调试器的缺点是不能使用定义,而不是枚举的优点是在调试器中可用。
而#define
相对于enum
的优势在于,您可以通过#ifdef
测试它的存在或在#if
中使用它的值,这使得它对于控制构建更有用-时间选择或提供的功能可能会出现在某些平台上,而其他平台上则不会。
较新版本的 gdb / gcc do 支持在调试器中使用#define
常量(我认为-gdb3
标志必须在编译时传递给gcc启用此功能)。
关于优化的部分不适用于任何进行链接时优化的编译器。【参考方案3】:
原因是大多数时候,您需要一个常量,而不是const
-qualified 变量。两者在 C 语言中并不相同。例如,变量不能作为 static
-storage-duration 对象的初始化程序的一部分,作为非 vla 数组维度(例如结构中数组的大小,或 C99 之前的任何数组)。
【讨论】:
除了“结构中的数组”之外,C99 中还有其他非 vla 数组吗? 具有静态存储持续时间的数组。复合文字数组。typedef
中使用的数组类型,具有文件范围。也许是其他人。【参考方案4】:
稍微扩展 R 的答案:fieldWidth
is not a constant expression;这是一个const
限定变量。它的 value 直到运行时才建立,因此它不能用于需要编译时常量表达式的地方(例如在数组声明中,或 switch
语句中的 case 标签中,等等。)。
对比宏FIELD_WIDTH
,预处理后扩展为常量表达式10
;这个值是在编译时已知的,所以它可以用于数组维度、案例标签等。
【讨论】:
所以const
的操作更像java 的final
,只允许一个赋值?
@C. Ross:出于所有实际目的,是的,尽管我认为语义上可能存在一些不平凡的差异(我在 C 代码中并没有太多使用 const
,而且我仍然处于 Java 学习的底层曲线)。【参考方案5】:
补充 R. 和 Bart 的答案:在 C 中定义符号编译时常量只有一种方法:枚举类型常量。该标准规定这些是int
类型。我个人会把你的例子写成
enum fieldWidth = 10 ;
但我猜 C 程序员之间的品味差异很大。
【讨论】:
【参考方案6】:虽然 const int 并不总是合适的,但如果您将某些东西定义为整数值,则枚举通常可以替代 #define。在这种情况下,这实际上是我的偏好。
enum FIELD_WIDTH = 16384 ;
char buf[FIELD_WIDTH];
在 C++ 中,这是一个巨大的优势,因为您可以将枚举限定在类或命名空间中,而不能限定 #define。
在 C 中,你没有命名空间,也不能在结构中限定枚举,甚至不确定你是否获得类型安全,所以我实际上看不到任何主要优势,尽管可能有一些 C 程序员会指出它给我。
【讨论】:
在 C++ 中,是否有任何“好”的方式来定义将表现为“int”的枚举,包括使用算术运算符?我知道可以定义运算符重载,并且可以在宏中完成这样的事情,以免太邪恶,但我不知道有任何简单的语言关键字可以用于类似的事情。有没有我不知道的存在?如果不是,这似乎是该语言的一个奇怪的遗漏,因为 C++ 应该允许轻松适应 C 程序。【参考方案7】:根据 K&R(第 2 版,第 211 页),“const 和 volatile 属性是 ANSI 标准的新属性”。这可能意味着真正古老的 ANSI 代码根本没有这些关键字,这实际上只是一个传统问题。 此外,它说编译器应该检测更改 const 变量的尝试,但除此之外它可能会忽略这些限定符。我认为这意味着某些编译器可能不会优化包含 const 变量的代码以在机器代码中表示为中间值(如 #define 所做的那样),这可能会花费额外的时间来访问远内存并影响性能。
【讨论】:
+1 用于引用 K&R,但 -1 用于回答它是关于优化而不是语言要求。净 0。 不,这意味着 const 和 volatile 是由 ANSI 标准 C 引入的。K&R 第二版实际上是在标准批准之前发布的。 很多 C 程序员使用strchr
来搜索字符而不是strtof
,因为直到C99 才添加strtof
。【参考方案8】:
一些 C 编译器会将所有 const
变量存储在二进制文件中,如果准备 a large list of coefficients 会占用嵌入式世界中的大量空间。
相反:使用const
允许闪现现有程序以更改特定参数。
【讨论】:
关于您的“相反”,这将是高度特定于实现的。一个好的实现会内联所有const
变量,并在它们的地址从未被占用时优化它们,就像它如何内联函数一样。
我已经看到了几个在多个平台(AVR、HCS08)的嵌入式系统中使用/滥用它的实现。它们被定义为const
,但有一些编译器指令导致它们存储在定义的内存地址。它们看起来就像* const
@R..:在 C 中,嵌入式级别的事情变得非常糟糕。你不能对 C 编译器做太多假设。
确实是一种巨大的滥用。正确的实现是#define myregister (*(volatile const uint32_t *)0xdecafbad)
或类似的。
@David,预处理器的内存?【参考方案9】:
在 C 中定义数字常量的最佳方法是使用 enum。阅读 K&R 的 The C Programming Language,第 39 页的相应章节。
【讨论】:
以上是关于为啥大多数 C 开发人员使用 define 而不是 const? [复制]的主要内容,如果未能解决你的问题,请参考以下文章