【问题标题】:Macro values defined using bit-shifts使用位移定义的宏值
【发布时间】:2018-05-24 10:35:18
【问题描述】:

我一直在浏览一个旧的源项目,试图让它编译和运行(这是一个已上传到 GitHub 的旧游戏)。我认为很多代码都是用 C-style/C-syntax 编写的(很多 typedef struct {...} 之类的),我注意到它们定义了以下样式的某些宏:

#define MyMacroOne (1<<0) //This equals 1
#define MyMacroTwo (1<<1) //This equals 2, etc.

所以我现在的问题是 - 有什么理由会以这种方式定义宏吗?因为例如0x01和0x02就是上面的数值结果。还是系统不会读取 MyMacroOne = 0x01 而是读取值为 (1


编辑:感谢您的所有投入!

【问题讨论】:

  • 搜索代码时,它们是如何使用的?
  • 这些是位掩码中的标志;它们都有一个位集。如果这就是您所追求的,那么就不太容易犯错误,例如这样写 2049 而不是 2048。
  • 使用上述简单模式可以更容易地发现错别字。十六进制编码不容易发现简单的拼写错误。
  • 正如 Wintermute 所说,这是典型的位掩码。每个位都有一个含义,以这种方式编写可以轻松发现哪个宏设置了哪个位。
  • 您可以立即看到应该在这样的掩码中设置哪个位。第 6 位不能意外使用 64 而不是 128。

标签: c++ c enums macros bit-shift


【解决方案1】:

它使定义位值更加直观且不易出错,尤其是在多位位域上。比如比较

#define POWER_ON     (1u << 0)
#define LIGHT_ON     (1u << 1)
#define MOTOR_ON     (1u << 2)
#define SPEED_STOP   (0u << 3)
#define SPEED_SLOW   (1u << 3)
#define SPEED_FAST   (2u << 3)
#define SPEED_FULL   (3u << 3)
#define LOCK_ON      (1u << 5)

#define POWER_ON     0x01
#define LIGHT_ON     0x02
#define MOTOR_ON     0x04
#define SPEED_STOP   0x00
#define SPEED_SLOW   0x08
#define SPEED_FAST   0x10
#define SPEED_FULL   0x18
#define LOCK_ON      0x20

【讨论】:

  • 是的,我承认它现在确实有道理,我只是习惯于看到更频繁地使用十六进制等效项。我猜这也没有性能差异?
  • @Cosmic 没有性能差异,除非编译器不好。这是对它的微不足道的优化。
【解决方案2】:

对人类来说很方便

例如

#define PIN0 (1u<<0)
#define PIN5 (1u<<5)

#define PIN0MASK (~(1u<<0))
#define PIN5MASK (~(1u<<5))

并且很容易查看是否有正确的位位置。它不会使代码变慢,因为它是在编译时计算的

【讨论】:

    【解决方案3】:

    您始终可以使用常量整数表达式移位来表示(倍数)2 的幂,即Multiple*(2 to the N-th power) = Mutliple &lt;&lt; N(有一些注意事项与您何时达到整数类型的保证大小限制和 UB 集合在* ) 并且很大程度上依赖于编译器折叠它们。

    由整数常量组成的整数表达式定义为integer constant expression。这些可用于指定数组大小、大小写标签和类似的东西,因此每个编译器都必须能够将它们折叠成一个中间体,即使在没有严格要求的情况下也不使用这种能力是愚蠢的。


    *例如:您可以使用1U&lt;&lt;15,但在 16 岁时您应该至少切换到 1L&lt;&lt;16,因为 ints/unsigneds 只需要至少 16 位并左移整数 by its width or into the place where its sign bit is is undefined (6.5.7p4)

    E1

    【讨论】:

    • “未定义行为”是否包括溢出(对于有符号整数)?
    • @CosmicM93 不管你怎么称呼它。重要的是要避免它,否则所有的赌注都会失败。
    【解决方案4】:

    宏只是替换文本。 Everywhere 宏被替换文本替换!!这很方便,特别是如果您想命名一些容易出错的常量。

    【讨论】:

      【解决方案5】:

      为了说明 (1&lt;&lt;0) 语法如何更实用,请考虑 Git 2.25(2020 年第一季度)代码库中的这个示例,它将一组位掩码常量的定义从 0ctal 文字移动到 (1U&lt;&lt;count) 表示法。

      参见 Hariom Verma (harry-hov)commit 8679577(2019 年 10 月 17 日)。
      (由 Junio C Hamano -- gitster -- 合并于 commit 8f40d89,2019 年 11 月 10 日)

      builtin/blame.c: 常量转换成位移格式

      签字人:Hariom Verma

      我们正在研究位域常量,在 Git 源代码的其他地方,这种情况是通过位移运算符而不是八进制数来处理的,这也更容易发现范围中的漏洞

      如果1&lt;&lt;5 丢失了:

      • 1&lt;&lt;41&lt;&lt;6 之间更容易发现它
      • 而不是在0200100 之间发现一个缺失的040

      所以而不是:

      #define OUTPUT_ANNOTATE_COMPAT  001
      #define OUTPUT_LONG_OBJECT_NAME 002
      #define OUTPUT_RAW_TIMESTAMP    004
      #define OUTPUT_PORCELAIN        010
      

      你得到:

      #define OUTPUT_ANNOTATE_COMPAT      (1U<<0)
      #define OUTPUT_LONG_OBJECT_NAME     (1U<<1)
      #define OUTPUT_RAW_TIMESTAMP        (1U<<2)
      #define OUTPUT_PORCELAIN            (1U<<3)
      

      【讨论】:

        猜你喜欢
        • 2021-11-18
        • 1970-01-01
        • 2023-02-04
        • 2018-07-03
        • 2012-07-01
        • 1970-01-01
        • 2018-06-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多