【问题标题】:Difference between global variables and variables declared in main in ARM CARM C中全局变量和main中声明的变量之间的区别
【发布时间】:2015-04-17 20:21:41
【问题描述】:

我一直在尝试在Keil 中使用C 为我的TM4C123G 编写一些测试代码,它使用ARM 微控制器。我不知道ARM assembly,但我过去曾为AVR microcontroller 编写过一些汇编代码。

如果我们在 C 中将变量声明为 global,而不是在 main 中声明,那么变量的值存储在哪里?

对于我们是否应该声明一个变量 global 而不是 main(在为微控制器编写 C 语言时)是否有一般准则?

【问题讨论】:

  • 全局变量绝不是个好主意。使其成为静态/文件范围。或完全避免。
  • 在很多情况下使用全局变量是安全且可以的。它们在大型代码库中肯定不好,但在微控制器的情况下,全局变量并不少见。
  • 实际上是我在网上找到的一些教程建议的,可能是为了使这些变量对每个函数可见并避免将它们作为参数传递,从而以 void 参数的 void 函数结束(即到底是什么原因,但我不确定是不是这个原因)。
  • @user3528438 - 所以标准委员会允许有害的语言功能牟利?没有意义……
  • 如果使用得当,全局变量是没有问题的。就像指针一样,就像手动内存管理一样。

标签: c arm global microcontroller keil


【解决方案1】:

ARM 中的全局变量会导致在调用函数的“池”区域中放置一个偏移量。一般来说,每个全局都需要自己的偏移量。如果您决定使用全局变量,请将它们放在一个结构中,以便可以通过单个池偏移量访问所有变量。

在函数级别定义的变量存在于堆栈中。这些,在合理的范围内,可以通过从堆栈寄存器更简单的偏移量来访问,并且往往是更有效的操作码,也许是缓存,具体取决于您使用的系统类型。

什么时候使用?在可维护性的全球与本地圣战之外,它归结为您的代码还想要做什么。如果在很多地方都使用了某个变量,并且您不想将其作为参数传递(这表明您的设计可能需要工作......),那么全局将是可行的方法。如果您需要跨多个汇编程序包访问此变量(再次设计...),那么也许全局也可以。

但大多数时候,我会使用局部变量。它们的线程安全性更高,并且从上面看,往往更有效率。它们也更容易维护,因为范围更有限 - 这也有助于编译器更好地完成工作。

【讨论】:

    【解决方案2】:
    1. 编译器会将全局变量放在“data”或“bss”内存段中。这种分配是永久的,所以变量永远不会失去它的价值。函数局部变量在堆栈上动态分配,并在函数返回时消失。
    2. 当变量必须在函数调用之间保持其值并且必须可由多个函数和/或文件中的代码访问时,使用全局变量。对其他一切使用函数局部变量。

    还有“静态”变量。它们的功能与全局变量相同,但具有更有限的命名空间。它们用作文件局部变量和函数局部变量,在函数调用之间保持其值。

    【讨论】:

      【解决方案3】:

      Globals 很好,它们在嵌入式产品中占有一席之地,尤其是在资源极为紧张的微控制器等嵌入式产品中。 Globals 可以让您轻松管理您的资源,而当地人非常活跃,充其量也很难不惹上麻烦。

      但是...就汇编而言,没有规则,没有真正意义上的本地与全局概念,严格来说是更高级别的语言。现在可能存在允许/强制执行此类操作的汇编语言的实现,请了解 C 是一种跨平台的标准(与其他标准一样),并不特定于某个平台。但是组装不仅没有标准,而且是特定于处理器的。汇编语言由汇编器(解析它并将其转换为机器代码的程序)定义。任何人和他们的兄弟都可以拼凑出一个汇编程序,并组成他们想要的任何语言或规则。

      一般来说,如果您试图在汇编中手动实现 C 代码,而不是让编译器来做(或者不是让编译器来做,至少看看它做了什么)。除非优化,否则您的全局变量将在 ram 中找到一个家。局部变量可能会或可能不会取决于优化在堆栈上获得一个位置,它们可能只是暂时存在于寄存器中,取决于可用寄存器的处理器数量 保留包含其他内容的寄存器与保留本地内容以支持保留寄存器中的其他内容。

      你应该只使用一些简单的函数(不一定是完整的程序),看看编译器做了什么。

      如果你想用纯汇编编写并且没有转换 C 的概念,你仍然会面临同样的困境,我是否将某些东西长期保存在寄存器中,我是否将它放在堆栈中的持续时间这段代码还是我给它分配一个永远存在的地址。

      我建议你思想开放,试着去理解为什么和为什么不,很多这样的规则,没有全局变量,小函数等等,不是因为事实,而是因为信仰,我信任的人告诉过我,所以我也宣扬它,并非总是如此,但有时你可以深入挖掘并发现恐惧是真实的或恐惧不是真实的,或者它可能在 30 年前适用,但现在不再适用,等等。例如,全局变量的替代方法是在 main() 或顶层的局部变量,您在嵌套时不断向下传递,这基本上意味着从资源角度来看它是全局变量。事实上,取决于编译器(特别是一个非常流行的编译器),一个主要级别的本地被传递下来,实际上在每个嵌套级别消耗资源,消耗的内存数量明显高于它刚刚被声明为全局的情况。另一方面,如果不是低效的内存消耗而是访问,谁可以弄乱该变量而谁不能弄乱,本地人使这变得很容易,相当多的懒惰,您可能会变得一团糟。必须注意全局变量,以免弄乱它们。请注意,从资源的角度来看,静态局部变量也是全局变量,它们在程序运行期间位于相同的 .data 空间中。

      C 不能死是有原因的,有很多原因。没有任何东西可以取代它。它基本上是每个新处理器/指令集的第一个编译器是有原因的。

      写一些简单的函数,编译,反汇编,看看产生了什么。为这些平台获取一些嵌入式/裸机应用程序,反汇编,看看编译器做了什么。无法优化的全局变量和静态局部变量在 .data 中获得地址。有时部分或全部传入参数获取堆栈位置,有时部分或全部局部变量也会消耗堆栈,如果您选择优化,则与编译器和优化有关。还取决于架构,可以执行内存和寄存器或内存到内存操作的 CISC 不必一直将内容移入和移出寄存器 RISC 通常必须专门使用寄存器或经常使用寄存器,但通常也有很多更多可用。所以编译器知道这一点并因此管理变量的主页。

      每个人都会将全局变量和静态局部变量放入 ram 中(除非他们可以优化出来)。无论如何,传统的 x86 都会将参数放入堆栈中,并且也会将本地参数放在那里并在那里访问它们。 mips 或 arm 将尝试最小化使用的 ram 数量,并依靠寄存器,尝试将某些变量独占保留在寄存器中而不消耗堆栈。

      汇编程序员,预编译器,使用了很多等效的全局变量,为每个变量选择一个地址。现在发布编译器,您可以选择以这种方式设置所有变量并且除了从调用返回之外几乎不使用堆栈,或者您可以像编译器一样编程并制定一些规则或简单地遵循编译器约定和保留一些一次性寄存器和其他寄存器,并靠在堆栈上进行保存和功能项的本地化。

      在一天结束时,尽管汇编没有全局与本地的概念,但它没有带符号与无符号与指针与数组的概念,或者任何类似高级语言的概念。

      【讨论】:

      • 我想他们不再教导函数永远不应该长到不适合打印页面的规则(对于那些玩字体游戏的人使用 12 pt 字体来绕过规则)。如果您有打印机,请举手……
      猜你喜欢
      • 1970-01-01
      • 2011-06-19
      • 2022-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-24
      相关资源
      最近更新 更多