【问题标题】:What to use instead of magic numbers in C [duplicate]在C中使用什么代替幻数[重复]
【发布时间】:2016-11-08 21:07:14
【问题描述】:

我目前在我的代码中使用静态常量,而不是使用"static const" vs "#define" vs "enum" 中提到的“幻数”。

void checkInvalidResponse (uint8_t response)
{
    static const uint8_t INVALID_RESP = 0xFF;

    if (response == INVALID_RESP)
    {
        /* Code for invalid response */
    }
}

然而,我认为使用 static const 会在 INVALID_RESP 的编译代码中消耗内存。该语句还将转换为机器代码,该机器代码从内存执行 LOAD,然后进行比较,而不是与作为指令的一部分提供的值进行比较。它是否正确?如果是这样,那么这个解决方案在速度和内存方面都不是最佳的,对吧?

我目前正在更改代码以使用#defines

void checkInvalidResponse (uint8_t response)
{
    #define INVALID_RESP 0xFF

    if (response == INVALID_RESP)
    {
        /* Code for invalid response */
    }
}

但是,由于#define 没有范围,#define 的剪切和粘贴方面的行为是否会在多个编译器之间保持一致?例如,如果 INVALID_RESP 稍后重新定义,那么重新定义之后的行中的任何代码都会使用新值吗?

我研究过的另一种方法是使用枚举。

void checkInvalidResponse (uint8_t response)
{
    typedef enum
    {
        INVALID_RESP = 0xFF
    } resp_t;

    if ((resp_t)response == INVALID_RESP)
    {
        /* Code for invalid response */
    }
}

但是,对枚举的类型转换会分配比所需更多的内存(处理器会进行 32 位(?)比较而不是 8 位比较)。

在代码中使用什么来代替幻数的最佳方法是什么?

【问题讨论】:

  • 你真的担心枚举基类型长于 1 个字节吗?
  • 聪明的人会在任何可能的地方建议 consts 超过 #defines。
  • 你可以undef#define,在那里你决定你已经完成了这个值......
  • static const 的效率是否低于#defines 完全取决于实现。
  • "(处理器将进行 32 位(?)比较而不是 8 位比较)。"没关系。如果一个实现使用 32 位的枚举,而它可能是 8 位,那么它可能有一个很好的理由。有趣的事实:8 位比较可能会更慢,但仍会使用完整的 32 位来存储操作数。所以请随意使用enum,忘记您​​完全想象的问题。在考虑性能时,请始终牢记这一点:在您可以衡量之前,这不是问题。(另外,_t 后缀是为 POSIX 保留的,因此不要将其用于您自己的东西。)

标签: c constants


【解决方案1】:

我认为在所有情况下,编译器,至少在打开 -O2 的情况下,都会生成相同的代码。欺骗编译器做愚蠢的事情变得非常困难。

通常情况下,幻数在一个公共标头中定义,并根据需要在整个代码中使用。

对您来说更大的问题是,这重要吗?这是否在您的代码的关键路径中,并且您将在非常高的时间内执行此操作?鉴于名字,我猜不是。

将定义移至头文件将使您的代码不那么分散注意力。在checkInvalidResponse 中,读者不太关心INVALID_RESPONSE 究竟代表什么,只关心测试通过或失败。

【讨论】:

    【解决方案2】:

    在 C 语言中,const 实体不是语言级别的常量,这极大地限制了 const 定义清单常量的可用性。请注意,这与生成代码的效率无关,而是与代码的基本有效性(“可编译性”)有关:在 C 语言中,const 实体根本不允许在需要常量的上下文中。

    出于这个原因,在 C 语言中,定义清单常量的唯一真正通用和通用的方法是 C 预处理器(即#define),enum 是另一种可行的替代方案,但仅限于适用的地方(一个显着的缺点是C 中的enum 常量是它们无条件签署int 类型的事实。

    只需使用#define,不要尝试“限定”常量。这样做很有意义。但是如果由于某种原因您需要限制#define'd 常量的范围,您可以始终使用#undef 来达到此目的。

    【讨论】:

    • 你能备份一下吗?据我记得,gcc 将常量放在 .text 部分
    • @doron:这与编译器放置它们的位置无关。这是关于在 C 语言中 const 实体不符合语言“常量”的事实。不允许在 case 标签、位域宽度、非 VLA 数组大小等中使用 const 实体。语言禁止这样做,代码根本无法编译。 cmets中链接的答案已经很好地涵盖了该主题。 OP 使用了带有if 的非代表性示例,它不需要常量。但如果他们改用switch/case 语句,const 的问题就会立即显现出来。
    • 谢谢。对我来说,这是 C 和 C++ 的意外差异。
    【解决方案3】:

    enum 由标准保证最多支持unsigned int 宽度。

    如果您使用#define,则没有隐式指定宽度,因为预编译器只会将符号替换为数字(或任何其他定义的),因此您可以将 L 附加到数字的末尾并保证long 值。

    坦率地说,我自己从来没有使用过大于整数的枚举...

    http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

    6.7.2.2 枚举说明符
    [...]
    约束
    定义枚举常量值的表达式应为整数常量表达式,其值可表示为 int。
    [...]
    每个枚举类型应与 char、有符号整数类型或无符号整数类型兼容。类型的选择是实现定义的,但应该能够表示枚举所有成员的值。

    【讨论】:

      【解决方案4】:

      这都是编译器和平台特定的。除非您真的需要挤出几个字节以适应 ROM,否则这并不重要。 使用编译器几乎总是比预处理器更不容易出错,因此请坚持使用 const。

      还要记住,如果更有意义,编译器总是允许内联的。

      【讨论】:

        猜你喜欢
        • 2013-12-03
        • 1970-01-01
        • 1970-01-01
        • 2020-04-01
        • 1970-01-01
        • 2010-09-08
        • 2014-11-18
        • 2016-10-16
        • 2013-06-08
        相关资源
        最近更新 更多