在嵌入式系统中使用全局变量

电器工程 C 嵌入式 固件
2022-01-24 02:12:37

我开始为我的产品编写固件,我是这里的新手。我浏览了许多关于不使用全局变量或函数的文章。在 8 位系统中使用全局变量是否有任何限制,或者它是一个完整的“不可以”。我应该如何在我的系统中使用全局变量,或者我应该完全避免它们?

我想就这个话题从你们那里得到宝贵的建议,以使我的固件更紧凑。

4个回答

只要您牢记@Phil 的指导方针,您就可以成功地使用全局变量。但是,这里有一些很好的方法可以避免它们的问题,而不会使编译的代码变得不那么紧凑。

  1. 将局部静态变量用于您只想在一个函数内访问的持久状态。

    #include <stdint.h>
    void skipper()
    {
        static uint8_t skip_initial_cycles = 5;
        if (skip_initial_cycles > 0) {
            skip_initial_cycles -= 1;
            return;
        }
        /* ... */
    }
    
  2. 使用 struct 将相关变量保持在一起,以便更清楚应该在哪里使用它们,在哪里不应该使用它们。

    struct machine_state {
         uint8_t level;
         uint8_t error_code;
    } machine_state;
    
    struct led_state {
        uint8_t red;
        uint8_t green;
        uint8_t blue;
    } led_state;
    
    void machine_change_state()
    {
        machine_state.level += 1;
        /* ... */
        /* We can easily remember not to use led_state in this function. */
    }
    
    void machine_set_io()
    {
        switch (machine_state.level) {
        case 1:
            PIN_MACHINE_IO_A = 1;
            /* ... */
        }
    }
    
  3. 使用全局静态变量使变量仅在当前 C 文件中可见。这可以防止由于命名冲突而导致其他文件中的代码意外访问。

    /* time_machine.c */
    static uint8_t current_time;
    /* ... */
    
    /* delay.c */
    static uint8_t current_time; /* A completely separate variable for this C file only. */
    /* ... */
    

最后一点,如果您在中断例程中修改全局变量并在其他地方读取它:

  • 标记变量volatile
  • 确保它对 CPU 来说是原子的(例如,对于 8 位 CPU 来说是 8 位)。

要么

  • 使用锁定机制来保护对变量的访问。

您不想在 8 位系统中使用全局变量的原因与您不想在任何其他系统中使用它们的原因相同:它们使对程序行为的推理变得困难。

只有糟糕的程序员才会被“不要使用全局变量”之类的规则所困扰。优秀的程序员了解规则背后的原因,然后将规则视为指南。

你的程序容易理解吗?它的行为是可预测的吗?在不破坏其他部分的情况下修改它的一部分是否容易?如果每个问题的答案都是肯定的,那么你正在走向一个好的计划。

您不应该完全避免使用全局变量(简称“全局变量”)。但是,您应该明智地使用它们。过度使用全局变量的实际问题:

  • 全局变量在整个编译单元中都是可见的。编译单元中的任何代码都可以修改全局。修改的后果可能会出现在评估此全局的任何地方。
  • 结果,全局变量使代码更难阅读和理解。程序员始终必须记住评估和分配全局变量的所有位置。
  • 过度使用全局变量会使代码更容易出错。

g_为全局变量的名称添加前缀是一种很好的做法。例如,g_iFlags当您在代码中看到带有前缀的变量时,您会立即认识到它是一个全局变量。

嵌入式工作中全局数据结构的优势在于它们是静态的。如果您需要的每个变量都是全局变量,那么当输入函数并在堆栈上为它们腾出空间时,您将永远不会意外地耗尽内存。但是,到那时,为什么要有功能呢?为什么不是一个处理所有逻辑和进程的大函数——比如一个不允许 GOSUB 的 BASIC 程序。如果您对这个想法足够深入,您将拥有一个典型的 1970 年代的汇编语言程序。高效,并且不可能维护和排除故障。

所以明智地使用全局变量,比如状态变量(例如,如果每个函数都需要知道系统是处于解释还是运行状态)和其他需要被许多函数看到的数据结构,正如@PhilFrost 所说,是你的功能可预测?是否有可能用永不结束的输入字符串填充堆栈?这些都是算法设计的问题。

请注意,静态在函数内部和外部具有不同的含义。https://stackoverflow.com/questions/5868947/difference-between-static-variable-inside-and-outside-of-a-function

https://stackoverflow.com/questions/5033627/static-variable-inside-of-a-function-in-c