这个策略是为了避免C中的全局变量吗?

时间:2017-04-19 13:04:40

标签: c embedded global-variables encapsulation

在我的(个人)嵌入式项目中,全局变量堆积如山。我需要从ISR(中断服务程序)和/或菜单系统(这样它们可以由用户修改)访问这些变量,但同时我想避免使用太多的全局变量。

由于它们可以按模块分组,我认为我可以将它们封装在自己的.c文件中,声明为staticstatic volatile,并向外界展示一些处理它们的函数。

以下内容:

module1.c中的

#include module1.h

static volatile int module1_variable;

int getModule1Var(void){
    return module1_variable;
}

void setModule1Var(int i){
    //some code to disable interrupts for atomic operations
    module1_variable = i;
    //some other code to re-enable interrupts
    return;
}

module1.h将包含函数原型,结构和所有其他元素,以使模块工作,当然除了静态变量定义

在main.c中

#include module1.h

void main(){
    //setting the variable value, could be done from a menu 
    setModule1Var(15);
    //remaining application code
}

void generic_ISR(){
    //trivial usage example
    doSomething(getModule1Var());
    return;
}

这个方案自然会扩展到其他模块。

现在我的问题是:
这是一个好方法吗?简单地拥有一堆全局变量是一样的吗?有任何重大缺点吗?

我还认为我可以使用某种混合,例如仍然具有全局变量以允许ISR直接操作(因为来自ISR的函数调用有时不受欢迎)以及其他情况下的函数。这会更好吗?

1 个答案:

答案 0 :(得分:2)

有几个问题:

  

这是一个好方法吗?

是。可能有一个论点是将ISR放在与它共享的访问函数和数据相同的模块中,将其视为访问函数本身,允许它直接读取数据而不需要访问包装器开销。

  

是否更好/更差/等于简单地拥有一堆全局变量?

更糟糕的是。有了全局,您将在何处放置访问互斥,数据验证或断点?它增加了模块耦合,应始终最小化。

  

有任何重大缺点吗?

不是真的。存在功能调用开销,这在ISR中可能是关键的;但如果这是一个问题,你就不会在ISR中调用printf()!您可以使用我之前关于将ISR放在数据模块中的建议,或者通过嵌入代码来减轻任何可能的性能损失 - 但是您的编译器可以自由地忽略它,并且如果启用调试可以这样做,并且也可以内联无论是否启用优化。一些编译器有一个强制的"内联扩展,不允许编译器忽略;但它不便携。

一个重要的好处是,如果变量是非原子的,您需要一些访问保护机制来确保一致的访问 - 并且通过访问功能最容易和安全地执行。在这种情况下,您可能有get / set函数禁用和重新启用访问中断,例如,或使用自旋锁(适用于ISR写入和正常上下文读取的位置 - 而不是相反的方式)在这里 - 不要无限期地锁定ISR!)。

  

我还认为我可以使用某种混合,例如仍然具有全局变量以允许ISR直接操作(因为来自ISR的函数调用有时不受欢迎)以及其他情况下的函数。这会更好吗?

不是真的 - 请参阅上面关于函数调用开销和ISR与数据的位置的观点。在ISR中调用函数的问题很少是时间开销问题,而是函数的实现者可能没有将其设计为从ISR安全有效地调用它并且它可能使用过多的堆栈,非确定性执行时间忙,等待或不重入或线程安全。例如,调用第三方或标准库函数可能是不明智的;调用您专门为此目的编写和设计的函数并符合您的特定约束并不一定是个问题。

你需要知道为什么全局性是一个坏主意,并且可以在Jack Ganssle的文章"A Pox on Globals"中找到避免它们的适当方法 - 这也是一个有趣的阅读。