【问题标题】:Making large constants in C source more readable?使 C 源代码中的大常量更具可读性?
【发布时间】:2012-06-14 04:33:54
【问题描述】:

我正在为微处理器编写一些代码。
它有几个大的、关键的常数。

#define F_CPU 16000000UL

在这种情况下,这是 CPU 频率。以赫兹为单位。

事实上,如果不手动在数字上移动光标,很难判断这是 1,600,000、160,000,000 还是 16,000,000。

如果我在数字#define F_CPU 16,000,000UL 中加上逗号,它会截断常量。

我使用过一些具有特定数字分隔符的深奥语言,旨在使大数字更具可读性(例如16_000_000,主要用于 MCU 的语言)。大的“幻数”在嵌入式产品中相当常见,因为需要它们来描述 MCU 如何与现实世界对话的各个方面。

C 中有这样的东西吗?

【问题讨论】:

  • “幻数”一词通常是指突然出现在代码中间的数字常量。例如:if (var == 16000000) ...(坏),而不是if (var == F_CPU) ...(好)。
  • 有类似的东西有帮助吗:#define F_CPU NUM_GROUPED_3ARGS(16,000,000UL)? stackoverflow.com/questions/10977260/…

标签: c embedded readability microprocessors


【解决方案1】:

是的,C 确实有预处理器分隔符:##

所以你可以写

#define F_CPU 16##000##000UL

与16000000UL具有完全相同的含义。 (与 16*1000*1000 等其他结构不同,您需要注意不要将它们放在乘法可能导致问题的地方。)

【讨论】:

  • 在什么情况下乘法会导致问题?
  • @jamesdlin 好吧,在具有更高优先级运算符的表达式中。例如,如果您有 #define FIRST 16000000#define SECOND 16*1000*1000 没有括号,~FIRST 将与 ~SECOND 不同。
  • 好的,那么我将改为“需要小心为表达式加上括号”,任何理智的 C 程序员通常都会这样做。
  • @jamesdlin 是的,就像这里的大多数答案一样。但不是全部。
  • ##实际上是cpp的concatenation operator,也可以用于其他用途。
【解决方案2】:

也许是这样的?

#define MHz(x) (1000000 * (x))
...
#define F_CPU MHz(16)

另外,我不喜欢#defines。通常最好有enums 或常量:

static const long MHz = 1000*1000;
static const long F_CPU = 16 * MHz;

【讨论】:

  • 这些在编译时被解决以设置各种常量的整个捆绑包,然后将其写入各种硬件寄存器,并因此为串行接口设置各种位时间等......你真的不想使用枚举或其他东西,因为它们可能会在某个地方的 RAM 中结束。这个单片机总共有2K RAM(和32K程序空间),相当珍贵。
  • 见鬼,看看这个#define#define Lin_set_btr_brr(bt,br) { U8 __jacq__; __jacq__ = LINCR; LINCR &= ~(1<<LENA); LINBTR = ((1<<LDISR) | (LBT_MASK & bt)); LINBRRH = (U8)((((((((U32)F_CPU*1000)<<1)/(((U32)br)*bt))+1)>>1)-1)>>8); LINBRRL = (U8)(( (((((U32)F_CPU*1000)<<1)/(((U32)br)*bt))+1)>>1)-1) ; LINCR = __jacq__; }
  • 注意:不是我写的,它来自实际的 MCU 文档。我正在尝试使其可读。
  • @FakeName 是的,有点疯狂 :) 低级编程有自己的规则。
  • 您的 MHz 宏应该将 x 括起来,这样MHz(1 + 2) 之类的东西才能正常运行。
【解决方案3】:

一种可能是这样写:

#define F_CPU (16 * 1000 * 1000)

或者

#define MHz (1000*1000)
#define F_CPU (16 * MHz)

编辑:其他人建议的 MHz(x) 可能更好

【讨论】:

  • 我实际上更喜欢这种表示法,因为它更接近实际指定时钟的方式。你不会说它是“MHz(16)”CPU,而是说它是“16 MHz”CPU。
  • 我最终使用了:#define MHz (1000UL*1000UL)#define F_CPU (16 * MHz)
  • 我必须将数字指定为UL,否则它们将被截断为 MCU 原生大小,即 8 位。结局很糟糕。
  • @Fake Name: int 在符合标准的实现中不能有 8 位大小,它至少需要 16 位。显然,它仍然以这种大小结束。
  • @Fake Name:我想我之所以提到它只是因为问题被标记为 C,但事实证明您不是在编程 C,而是在编程与 C 相关的其他语言. 与由于 PC 等编译器中的小缺陷而获得的更常见的 C 变体不同,大多数偶然的路人不会对 8 位处理器的类 C 语言了解太多,当然也不知道哪种你需要支持的编译器,因为你没有说。可能会影响答案。虽然不是这个答案,因为即使在实际的 C 中,您至少需要一个 long 来保证 32 位。
【解决方案4】:

您可以将常数写为计算的结果(以16*1000*1000 为例)。更好的是,您可以定义另一个宏 MHZ(x),并将您的常量定义为 MHZ(16),这将使代码更加自我记录 - 以创建名称空间冲突概率为代价。

【讨论】:

    【解决方案5】:
    // constants.h
    #define Hz   1u              // 16 bits
    #define kHz  (1000u  *  Hz)  // 16 bits
    #define MHz  (1000ul * kHz)  // 32 bits
    
    // somecode.h
    #define F_CPU (16ul * MHz)   // 32 bits
    

    注意事项:

    • int 在 8 位 MCU 上是 16 位。
    • 尽可能将 16 位文字优化为 8 位文字(使用 8 位指令)。
    • 有符号整数文字很危险,尤其是在与位运算符混合时,这在嵌入式系统中很常见。默认情况下使所有内容都未签名。
    • 考虑使用变量表示法或 cmets 来指示常量是 32 位,因为 32 位变量在大多数 8 位上非常慢。

    【讨论】:

      【解决方案6】:

      另一种方法是在更通用的宏中使用 ## 预处理器运算符

      #define NUM_GROUPED_4ARGS(a,b,c,d) (##a##b##c##d)
      #define NUM_GROUPED_3ARGS(a,b,c)   (##a##b##c)
      
      #define F_CPU NUM_GROUPED_3ARGS(16,000,000UL)
      
      int num = NUM_GROUPED_4ARGS(-2,123,456,789); //int num = (-2123456789);
      int fcpu = F_CPU; //int fcpu = (16000000UL);
      

      这在某种程度上是所见即所得,但不能免于误用。例如。你可能会抱怨编译器

      int num = NUM_GROUPED_4ARGS(-2,/123,456,789);  //int num = (-2/123456789); 
      

      但它不会。

      【讨论】:

        【解决方案7】:

        你可以使用科学记数法:

        #define F_CPU 1.6e+007
        

        或者:

        #define K 1000
        
        #define F_CPU (1.6*K*K)
        

        【讨论】:

        • 科学记数法F_CPU 的类型为double,这会引入一些潜在的陷阱。
        • 浮点数可能不是一个选项,因为这适用于 8 位 MCU。
        • 我必须停下来思考以确保 1.6e7 == 16,000,000。 16.0e6 会更清晰。但是,最好还是坚持使用纯整数。
        【解决方案8】:

        将常量定义为:

        #define F_CPU_HZ 16000000UL
        

        这样您就知道其中的数据类型。在我们的软件中,我们有一些外围设备需要设置各种预分频器,所以我们有一个 #defines 像这样:

        #define SYS_CLK_MHZ    (48)
        #define SYS_CLK_KHZ    (SYS_CLK_MHZ * 1000)
        #define SYS_CLK_HZ     (SYS_CLK_KHZ * 1000)
        

        【讨论】:

          猜你喜欢
          • 2019-06-05
          • 1970-01-01
          • 2012-07-02
          • 2018-11-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多