为啥大多数 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.罗斯:一致性。所有清单常量通常用#defines 定义。 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? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥不使用开发配置而不是临时配置?

为啥反应通常应该是产品依赖而不是开发依赖

为啥 Loki 库没有得到更广泛的使用?

为啥微软用c、c++而不是c#来开发windows? [关闭]

为什么游戏引擎大多选择使用 c++ 而不是 c 开发?

VK_NULL_HANDLE 始终为 0;为啥不是 nullptr?