ARM C中main中声明的全局变量和变量的区别

Difference between global variables and variables declared in main in ARM C

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

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

是否有关于我们是否应该声明变量 global 而不是 main 的一般准则(当涉及到为微控制器编写 C 语言时)?

全局变量很好,它们有一席之地,尤其是在资源极其紧张的微控制器等嵌入式产品中。全局变量使您可以轻松管理您的资源,而本地变量非常动态并且充其量也很难不惹麻烦。

但是......就汇编而言,没有规则,也没有真正意义上的本地与全局的概念,严格来说这是一种高级语言。现在可能有汇编语言的实现 allow/force 这样的事情,请理解 C 是跨平台且不特定于一个平台的标准(与其他标准一样)。但是汇编不仅没有标准,而且是特定于处理器的。汇编语言由汇编程序(解析它并将其转换为机器代码的程序)定义。任何人和他们的兄弟都可以搞出一个汇编程序并编写他们想要的任何语言或规则。

一般来说,如果您尝试在汇编中手动实现 C 代码,而不是让编译器来做(或者不是让编译器来做,至少看看它做了什么)。除非经过优化,否则您的全局变量将在 ram 中安家。局部变量可能会或可能不会取决于优化在堆栈中占有一席之地,它们可能只是暂时存在于寄存器中,这取决于可用寄存器的处理器数量在保留包含其他内容的寄存器与保留局部变量以保留局部变量之间进行权衡寄存器中的其他内容。

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

如果你想用纯汇编语言编写并且没有转换 C 的想法,你仍然会面临同样的困境:我是否将某些东西长期保存在寄存器中,我是否将它放在堆栈中以备不时之需?这块代码的持续时间,还是我为它分配一个地址,让它永远存在。

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

C不能死是有原因的,有很多原因。没有什么可以取代它。它基本上是每个新 processor/instruction 集的第一个编译器是有原因的。

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

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

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

归根结底,汇编语言没有全局与本地的概念,就像它没有有符号、无符号、指针、数组或任何高级语言所具有的概念一样。

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

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

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

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

  1. 编译器会将全局变量放在"data"或"bss"内存段。此分配是永久性的,因此变量永远不会丢失其值。函数局部变量在堆栈上动态分配,并在函数 returns.
  2. 时消失
  3. 当变量必须在函数调用之间保持其值并且必须可由多个函数 and/or 文件中的代码访问时,使用全局变量。对其他一切使用函数局部变量。

还有"static"个变量。它们的功能与全局变量相同,但名称空间更有限。它们用作文件局部变量和函数局部变量,在函数调用之间保持它们的值。