【问题标题】:Embedded C: Risk of Initializing global data嵌入式 C:初始化全局数据的风险
【发布时间】:2021-06-25 08:31:10
【问题描述】:

c 语言中的一般问题:

在声明中初始化数据是否安全? 示例:

static unsigned char myVar =5u;

这个值是否会被启动代码覆盖?

【问题讨论】:

  • 这很安全,但是如果不看一下“启动代码”,就无法回答您的其他问题。我的意思是,main 或其他方法覆盖这个值是完全合法的。
  • 事实上我正在评估两种方法:我在帖子中描述的初始化和使用 init 函数的初始化。哪种方式最安全?
  • 你是什么意思,它们都是安全的,但没有什么能阻止你(或你的同事)在声明中将值初始化为 5u,然后在声明中用 13u 覆盖它main()。只有人的手和糟糕的想法会成为初始化变量的错误来源。
  • 您为什么认为它不安全?你也可以问,假设aa = 2 + 2; 之后包含 4 是否安全。
  • @Jabberwocky 我听说过在main 启动之前可以覆盖全局变量的情况,我相信嵌入式系统中存在一些链接器技巧。我必须四处寻找确认。

标签: c embedded


【解决方案1】:

通常,嵌入式系统微控制器项目有两种风格,IDE 通常让您选择一种:

  • 符合标准 C(有时被混淆的工具供应商称为“ANSI”)。
  • 最小化启动。

前一个标准 C 兼容项目要求在调用 main() 之前初始化所有具有静态存储持续时间的变量,例如在文件范围和/或使用关键字 static 声明的变量.此初始化发生在启动代码(“C 运行时”/“CRT”)中。在这样的系统上,myVar = 5u; 保证被启动代码写入(而不是覆盖)。它将值 5 从闪存复制到 RAM。

后者,“mimizined”/“fast”启动版本不严格符合 C 标准。在这样的项目中,所有静态存储持续时间变量的初始化代码都被简单地删除了。这可以减少从重置到调用 main() 的时间。在这样的系统上,nothing 将执行 static unsigned char myVar =5u; 代码 - 即使您明确初始化了变量,您的变量仍保持未初始化和不确定。您必须在“运行时”手动设置它,这通常是通过一些初始化“构造函数”代码完成的。
如果您有属于foo.cstatic uint8_t foo_count;,那么foo 模块将必须提供一个函数foo_init(),从该函数执行代码foo_count = 5;

由于“最小化启动”版本在嵌入式系统中非常常见,因此依赖静态存储持续时间变量的默认初始化通常被认为是危险的,以防代码被移植到这样的系统。

【讨论】:

    【解决方案2】:

    我希望你的启动代码在 main 之前运行。话虽如此,当您声明变量静态时,变量范围绑定到该翻译单元(somefile.c) 的范围,所以是的,您可以覆盖它,但其他单元很难做到这一点(直接内存分配)嵌入式系统的规则是避免使用全局变量,但如果您最常使用,请将全局变量声明为头文件中最有意义的位置。

    【讨论】:

    • 它是应用初始化的启动代码。
    • 更正运行时设置处理这个问题。
    【解决方案3】:

    或者,您可以将其替换为 #define。

    #define MY_VAR 5U
    

    【讨论】:

    • 不,你不能。那是字面常量,而不是变量。
    【解决方案4】:

    启动代码负责应用初始化(您认为这是在哪里完成的?),所以是的,它是安全的并且就初始化最佳实践而言。

    如果没有初始化,静态或全局的值应该为零。一些嵌入式系统中的启动代码故意省略零初始化以最小化启动时间并不是闻所未闻的。通常这不会是默认的启动代码,您必须有意识地决定使用它。对于可能不知道这种非标准行为的后期维护者来说,这是不安全和不公平的。在大多数情况下,这也是一种不必要且过早的优化。尽管如此,即使单位为零,您也可以初始化所有此类变量,以防止此类非标准启动(同时也使其毫无意义,通常是这样)。

    不是最佳实践是首先使用全局变量 (https://www.embedded.com/a-pox-on-globals/)。虽然公平地说,在这种情况下,有问题的 myVar 并不是真正的全球性。

    【讨论】:

      【解决方案5】:

      这可能与原问题没有直接关系,但我认为值得一提:

      使用来自不同翻译单元的另一个全局变量初始化全局变量是不安全的。例如:

      在交流中

      unsigned var_from_a = 5u;
      

      在公元前

      extern unsigned var_from_a;
      unsigned var_from_b = var_from_a;
      

      不保证var_from_b 变成5u,因为翻译单元的初始化顺序没有定义。如果运气不好,b.c 在 a.c 之前处理,var_from_b 可能会变成0 或其他垃圾值,而 a.c 稍后处理,var_from_a 正确初始化为 5u

      我不确定如果将 var_from_a 定义为 const 是否会改变这种行为。

      【讨论】:

      • var_from_a 不是整数常量表达式,因此无论如何它都不会编译。
      猜你喜欢
      • 2020-05-14
      • 2017-06-17
      • 2012-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多