【问题标题】:Is there difference between a static const and const variable inside a c++ class in terms of its storagec++ 类中的静态 const 和 const 变量在存储方面是否存在差异
【发布时间】:2019-09-16 10:29:30
【问题描述】:

这是我拥有的代码的 sn-p:

class modbus {
public:
  static const uint8_t modbusHeader = 2;
  static const uint8_t modbusCRC = 2;
  static const uint8_t modbusPDU = modbusHeader + modbusCRC;
  static const uint8_t exceptionBase = 0x80;
  static const uint32_t transmitTimeout = 5000;
};

它为我需要在类中创建的 modbus 数据包定义了一些大小。我在嵌入式环境中工作,因此始终存在尺寸优化和考虑因素。因此,我真的希望在闪存的只读部分中只出现一次这些常量值。

我选择将这些变量设置为static,但这有必要吗?编译器是否会推断这些值只需要在二进制文件中保存一次,因此当我删除 static 关键字时只包含一次?

【问题讨论】:

  • 如果你删除static,语义就会改变。
  • 是的,但我觉得它是不必要的,因为它不是可变变量,因此不能传达多个对象访问单个变量的含义。我认为如果我删除static 它将消除一些噪音,因为没有机会出现竞争条件等。但我担心空间优化。
  • 如果你只从变量中读取,为什么会有竞争条件?这是什么系统?你提到闪光灯。它是微控制器还是伪装的 PC?
  • 您的问题缺少一些关键信息。如果您删除 static 关键字,则该语言要求类的每个对象都有自己的常量副本(每个对象可能有不同的值)。编译器如何推断这些值只需要保存一次?

标签: c++ embedded


【解决方案1】:

我想,从技术上讲,如果编译器知道您从未在 modbus 上执行过 sizeof,并且从未通过不同的 modbus* 指针获取这些成员的地址,并且知道它们只是用完全相同的平凡值初始化,它可能使用“as-if”规则将它们合并为一个,并在存储方面将它们从类中删除。 (如果它不能保证其中之一,就会违反语言规则。)

但这是一项艰巨的任务(特别是当您考虑多个翻译单元时),并且不会真正有用。

所以没有。我不认为这会发生。

你确实应该把这些东西做成static const(也许还有一些constexpr)。

【讨论】:

    【解决方案2】:

    代码

    #include <cstdint>
    
    class modbus {
    public:
      static const uint8_t modbusHeader = 2;
    };
    
    int main() {
        return modbus::modbusHeader;
    }
    

    结果

    main:
            pushq   %rbp
            movq    %rsp, %rbp
            movl    $2, %eax
            popq    %rbp
            ret
    

    和

    #include <cstdint>
    
    class modbus {
    public:
      const uint8_t modbusHeader = 2;
    };
    
    int main() {
        return modbus().modbusHeader;
    }
    

    结果

    modbus::modbus() [base object constructor]:
            push    rbp
            mov     rbp, rsp
            mov     QWORD PTR [rbp-8], rdi
            mov     rax, QWORD PTR [rbp-8]
            mov     BYTE PTR [rax], 2
            nop
            pop     rbp
            ret
    main:
            push    rbp
            mov     rbp, rsp
            sub     rsp, 16
            mov     BYTE PTR [rbp-1], 0
            lea     rax, [rbp-1]
            mov     rdi, rax
            call    modbus::modbus() [complete object constructor]
            movzx   eax, BYTE PTR [rbp-1]
            movzx   eax, al
            leave
            ret
    

    至少这里有很大的不同。我使用 g++ 9.2 和 -O0

    【讨论】:

    • 当您尝试执行(和使用)modbus::modbusHeader(在 static-class-member 版本中 modbusHeader 在类定义中“初始化”)时,它会变得很有趣。编译器(g++ 和 clang)可以让你毫无怨言地做到这一点,但它无法链接。它仅在您在类定义之外初始化静态类成员时链接。我想知道是编译器损坏还是语言规范损坏。
    • 它是基于闪存的嵌入式系统,而不是 x86 PC。反汇编完全无关紧要,这里最重要的是链接。
    • -O0 有助于调试,但在描述实际系统行为时有害。
    【解决方案3】:

    如果你坚持使用 C++,你必须const-qualify 类的一个对象,而不是类的成员。然后,您必须确保具有静态存储持续时间的 const 变量以您给定目标的闪存结束。通常,工具链有一个名为“flash-something”的构建。它与链接器而不是编译器有关。如果该部分正常工作,则该类的对象是否为static 无关紧要,只要该对象在文件范围内声明(静态存储持续时间)。

    你不应该做的是,只对 const 限定类成员,而不是它们所在的对象。因为这可能会导致静默 C++ 膨胀以由调用的默认构造函数的形式保留在可执行文件中“阴极射线管”。您可以通过单步执行 CRT 中处理调用具有静态存储持续时间的对象的构造函数的部分来验证这一点。

    至于让成员static,你应该只需要为了像单身这样的目的这样做,而不是为了让成员最终成为flash。

    【讨论】:

    • ...或者您可以只用 C 编写代码,然后休息一天。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-18
    • 1970-01-01
    • 2011-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-10
    相关资源
    最近更新 更多