这是在嵌入式软件中使用 C 避免全局变量的有效/良好技术吗?

Posted

技术标签:

【中文标题】这是在嵌入式软件中使用 C 避免全局变量的有效/良好技术吗?【英文标题】:Is this a valid/good technique for avoiding global variables with C in embedded software? 【发布时间】:2013-09-14 01:19:42 【问题描述】:

我对避免全局变量的话题进行了长期而认真的研究,并提出了一种我在搜索中从未真正见过的方法,这让我相信这可能不是一个好方法这样做(或者我只是没有正确地措辞我的搜索)。

作为一个例子,我有这样的事情:

int16_t DataProcessing(uint8_t CallType, struct DataStruct *DataIO)


     int16_t RetVal;
     static struct DataStruct StaticDataStuct;

     switch (CallType)
         
         case FIRSTCASE: RetVal = FirstCaseProcessing(&StaticDataStuct,DataIO); break;
         case SECONDCASE: RetVal = SecondCaseProcessing(&StaticDataStuct,DataIO); break;
         ...
         etc
         ...
         

     return RetVal;



int16_t FirstCaseProcessing(struct DataStruct *StaticData, struct DataStruct *NewData)


// Do what you need to do here


调用的任何其他例程的想法相同。

我还完成了调用 DataProcessing() 的包装函数,这使得整个内容更易于阅读,并且对于将来使用它的新人来说更容易。所以,例如:

int16_t FirstCase(uint8_t Address, uint16_t Data)


     struct DataStruct NewData;

     NewData.Address = Address;
     NewData.Data= Data;

     return DataProcessing(FIRSTCASE, &NewData);


所以,看起来不错的是,除了 UART 和定时器等中断之外,我根本没有全局变量。 (我仍然认为尽快进入和退出中断比让中断调用将数据存储在某个静态变量中要好,但很高兴被说服。)

[也许]不好的是,为了避免全局变量并使其更具可读性,我通过三个函数传递内容(假设不只是 me 发现它可读!)

我会说我已经获得了一个 72MHz 嵌入式 32 位处理器来完成一个 8 位处理器可以完成的工作,运行速度只是速度的一小部分(假设它有足够的 RAM)。所以速度不是问题,尽管我对当速度可能成为更多问题时这是否是一种好的风格感兴趣。

我已经看到了 C++ 风格,即拥有 .c 文件并拥有该 .c 文件中的任何函数都可以看到和访问的静态变量(但外部文件不能),使用访问器函数将值/指针等传入和传出,但他们似乎使用了我认为对文件“全局”的变量(或文件的本地变量,具体取决于您如何看待它!)。此外,可以有一个函数存储一个静态变量,并简单地将指向该静态变量的指针传递给任何想要访问它的东西。我想知道这是否会成为 OTT?

我的想法看起来好/坏/可怕吗?

非常感谢您提供的任何建议和所有 TL;我可能会得到的 DR。 ;~)

【问题讨论】:

DataStruct 里面有什么? 程序的特定部分所需的任何东西。例如其中之一是端口状态、计时器值、标志、卡号、端口号。 了解您为什么反对文件范围的全局变量会很有用?假设您的所有函数都在同一个文件中,那么在函数中拥有静态结构与在文件中拥有静态结构可以忽略不计。任何一种方法都存在多线程问题;但这可能相关也可能不相关。如果可能的话,我认为我们需要了解更多背景信息。 我不一定反对文件范围的全局变量。我看到有些人说这些是可以接受的,而另一些人则说避免它们。我想知道我是否能找到一种普遍接受的方式。这里面没有任何多线程;它是纯粹的运行完成风格的软件。 那就试试动态全局变量吧。它是在程序执行时创建的变量列表,通过函数通过标识符访问它们。 【参考方案1】:

OP:我的想法看起来好/坏/可怕吗?

OP还不是OTT。

在嵌入式设计中避免使用全局变量是一个很好的目标,主要是为了维护。信息隐藏(使数据成为函数或对象的本地数据)是控制无数交互并使调试更容易的关键。对于 OP 速度更快(并且可能内存更大)的处理器尤其如此。


另一种选择 - 在文件范围内隐藏数据。

OP 解决方案在DataProcessing() 处显示分层,命令和输入/输出参数已给出,DataStruc 的详细信息在此级别已知。

我的目标是更多的数据驱动方法,使用指针或索引和一组例程。假设在一个嵌入式程序中,我最多需要一个 Sally 变量的 Sally_N 实例。这里我的数据不是全局的,而是隐藏在 Sally.c 的文件范围内。因此,数据及其详细字段远离使用它的更高级别代码。在 OP 的方法中,DataStruct 的细节被更高级别的函数DataProcessing() 知道。

// Sally.h
struct Sally_t;  // Does not expose the fields
extern struct Sally_t *Sally_Init(...);
extern void Sally_DoThis(struct Sally_t *, ...);
extern void Sally_DoThat(struct Sally_t *, ...);

// Sally.c
struct Sally_t  int a, ... ;  // Structure details
static struct Sally_t Sally_Data[Sally_N];// file scope prevents global exposure
struct Sally_t *Sally_Init(...);
void Sally_DoThis(struct Sally_t *, ...);
void Sally_DoThat(struct Sally_t *, ...);

【讨论】:

以上是关于这是在嵌入式软件中使用 C 避免全局变量的有效/良好技术吗?的主要内容,如果未能解决你的问题,请参考以下文章

怎么在汇编中引用c语言的全局变量

在C语言中啥是全局变量?用一个小程序段说明下,谢谢,

C语言中,宏替换与定义全局变量的区别是啥?

如何访问 C 中的阴影全局变量?

计算机系统篇之链接(11):为什么要避免在 C/C++ 中使用全局变量

计算机系统篇之链接(11):为什么要避免在 C/C++ 中使用全局变量