如何在两个函数之间共享变量而不将其声明为C语言中的全局变量

时间:2016-03-17 13:27:47

标签: c embedded

我一直在阅读很多答案,对此有很多意见,但我找不到能够回答我问题的代码(我发现很多代码都回答“如何分享”变量通过声明“)

情况如下:

  • 使用嵌入式系统
  • 使用IAR工作台系统
  • STM32F4xx HAL驱动程序
  • 声明全局变量不是一个选项(编辑:与保持内存大小有关的事情,因此局部变量在范围的末尾消失,但全局变量保持不变。局部变量作为输出发送出去,所以我们因为我们不需要它们而立即丢弃它们。)
  • C语言
  • 如果这很重要:两个<。li>中包含2个.c文件和1个.h

现在已经不在了,让我写一个例子。

?maxperiod(13,911,11584577) True - 监控

file1.c

void function(){ uint8_t varFlag[10]; // 10 devices for (uint8_t i = 0; i < 10; i++) { while (timeout <= 0){ varFlag[i] = 1; // wait for response. We'll know by the ack() function // if response back from RX, // then varFlag[i] = 0; } - RX方

file2.c

4 个答案:

答案 0 :(得分:3)

您共享值或数据,而不是变量。 Stricto sensu,变量在运行时不存在;只有编译器知道它们(最多使用-g,它可能会在调试部分中放置一些元数据,例如偏移和类型的本地化 - 通常在可执行文件的生产代码中被删除)。 Ther链接器符号表(用于全局变量)可以并且通常在嵌入式发布的ELF二进制文件中被剥离。在运行时,您有一些数据段,可能是call stack由调用帧组成(在某些插槽中包含一些局部变量,即它们的值)。在运行时,只有位置是相关的。

(某些嵌入式处理器对其调用堆栈有严格限制;其他嵌入式处理器的RAM或暂存器内存有限;因此了解您所针对的实际处理器和ISA会有所帮助,并且你有多少RAM的想法

所以有一些全局变量保留这些共享值(可能间接通过一些指针和数据结构),或通过参数传递这些值(可能是间接的,同样......)。

因此,如果您想共享十个字节varFlag[10]数组:

  • 您似乎不希望将uint8_t varFlag[10];声明为全局(或static)变量。你确定你真的不应该(这十个字节必须坐在某个地方,而且他们确实消耗了一些RAM,可能在你的调用堆栈中......)?

  • 传递varFlag(数组,作为参数传递时衰减到指针)作为参数,所以可能声明:

    void ack(uint8_t indexDevice, uint8_t*flags);
    

    并从ack(3,varFlag) ...

    致电function
    • 或声明一个全局指针:

      uint8_t*globflags;
      

并在函数开头设置它(使用globflags = varFlag;),将varFlag声明为局部变量,并在该函数的末尾清除if(使用globflags = NULL;)。 / p>

我建议您查看编译器生成的汇编程序代码(使用GCC编译,可以使用gcc -S -Os -fverbose-asm -fstack-usage ....编译。我还强烈建议您让同事审核您的代码......

PS。也许您应该使用GCCClang/LLVM作为交叉编译器,也许您的 IAR 实际上正在使用这样的编译器......

答案 1 :(得分:1)

将函数放入单独的translation unit并使用static变量:

static type var_to_share = ...;

void function() {
    ...
}

void ack() {
    ...
}

注意我说翻译单位,不是文件。你可以做一些#include魔术(以最干净的方式)来保持两个功能定义。

答案 2 :(得分:1)

您不使用全局变量的论点:

  

与保持内存大小相关的一些事情,因此局部变量在范围的末尾消失,但全局变量保持不变。局部变量作为输出发送出去,因此我们立即丢弃它们,因为我们不需要它们

生命周期范围混淆。无论范围(或可见性)如何, static 生命周期的变量都会永久占用内存。具有全局范围的变量也恰好是静态分配的,但是任何其他静态变量也是如此。

为了跨上下文共享变量,它必须是静态的,因此通过避免全局变量不会节省内存。然而,有许多其他更强大的论据可以避免全局变量,你应该阅读Jack Ganssle撰写的A Pox on Globals

C支持三个级别的范围:

  • 功能(在功能内)
  • translation-unit(静态链接,函数外)
  • 全球(外部联动)

其中第二个允许变量在同一源文件中的函数之间直接可见,而外部链接允许多个源文件之间的直接可见性。但是,在大多数情况下,您希望避免直接访问,因为这是全局变量的基本问题的根源。您可以使用访问器功能执行此操作;要使用您的示例,您可以添加包含以下内容的file3.c

#include "file3.h"

static uint8_t varFlag[10]; 
void setFlag( size_t n )
{
    if( n < sizeof(varFlag) )
    {
        varFlag[n] = 1 ;
    }
}

void clrFlag( size_t n )
{
    if( n < sizeof(varFlag) )
    {
        varFlag[n] = 0 ;
    }
}

uint8_t getFlag( size_t n )
{
    return varFlag[n] == 0 ? 0 : 1 ;
}

使用关联的头文件3.h

#if !defined FILE3_INCLUDE
#define FILE3_INCLUDE

void setFlag( size_t n ) ;
void clrFlag( size_t n ) ;
uint8_t getFlag( size_t n ) ;

#endif

包含file1.c和file2.c,以便他们可以通过访问者函数访问varFlag[]。好处包括:

  • varFlag[]无法直接访问
  • 这些功能可以强制执行有效值
  • 在调试器中,您可以在代码中的任何位置设置专门设置,清除或读取访问表单的断点捕获。
  • 隐藏内部数据表示

批判性地避免全局变量不会保存你的内存 - 数据仍然是静态分配的 - 因为你不能得到任何东西varFlag[]必须存在,即使它是不可见。也就是说,关于内部表示的最后一点确实提供了存储效率的潜力,因为您可以将标志表示从uint8_t更改为单个位标志,而无需更改数据接口或访问访问代码:

#include <limits.h>
#include "file3.h"

static uint16_t varFlags ;

void setFlag( size_t n )
{
    if( n < sizeof(varFlags) * CHAR_BIT )
    {
        varFlags |= 0x0001 << n ;
    }
}

void clrFlag( size_t n )
{
    if( n < sizeof(varFlags) * CHAR_BIT )
    {
        varFlags &= ~(0x0001 << n) ;
    }
}

uint8_t getFlag( size_t n )
{
    return (varFlags & (0x0001 << n)) == 0 ? 0 : 1 ;
}

还有更多机会生成健壮的代码,例如,您可能只将读访问器(getter)公开显示并隐藏,以便除一个翻译单元之外的所有代码都具有只读访问权限。

答案 3 :(得分:0)

不幸的是,您无法使用C。

做到这一点的唯一方法是要有毅力。