嵌入式 C:初始化全局数据的风险

Embedded C: Risk of Initializing global data

C语言的一般问题:

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

static unsigned char myVar =5u;

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

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

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

#define MY_VAR 5U

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

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

什么是 而不是 最佳实践是首先使用全局变量 (https://www.embedded.com/a-pox-on-globals/)。尽管公平地说,在这种情况下,所讨论的 myVar 并不是真正的全球性。

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

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

在a.c

unsigned var_from_a = 5u;

在b.c

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 是否会改变这种行为。

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

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

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

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

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