How do I share variables between different .c files?
关于C声明的初学者问题:
在.c文件中,如何使用在另一个.c文件中定义的变量?
- 我改变了你的头衔。试着让标题更具描述性,这样1)能够回答它的人可以很容易地看到你在问什么;2)让其他有相同问题的人能够找到这个问题,这样他们就不必再问了。在查看问题列表时,我们不需要知道这是一个初学者的问题,但我们确实需要知道这个问题是关于什么的。只是未来问题的提示。:)
C:
在菲律宾
在Fielb C中:
1 2
| #include"fileA.h"
myGlobal = 1; |
这就是它的工作原理:
- 变量存在于filea.c中
- fila.h告诉世界它存在,它的类型是什么(int)
- fileb.c包含filea.h,以便编译器在fileb.c尝试使用它之前了解myglobal。
- 当变量是静态的,它会给我一个链接错误,"不可解析的符号",如何处理?
- @JFQ:非阶级语境中的"静态"是指"仅限于这个翻译单元"。它本质上与"外部"相反。你不能同时拥有两者。
- 只是一句话——如果变量的所有者是main.c文件,而没有main.h,怎么办?
- @Arbitter:如果没有头文件,就不能共享变量(除非将extern int myGlobal;直接放入要访问myGlobal的源文件中,但这严重违反了dry)。如果需要从main.c共享全局,请创建main.h!
- @里奇:不确定是否允许我做主菜。H,但是我会把它放在另一个文件里,谢谢!
- @里奇辛德尔:我又一次-在多个文档中有多个char i;的问题。我在多个.c文件中声明char i;,而不将extern char i;放在相应的.h文件中,因为我希望每个文件都有一个用于迭代目的的i,那么让某个文件拥有它更好吗?
- 多个.c文件可以有自己的char i;文件,而不需要共享它们。这很正常。如果您在编译或运行您所拥有的内容时遇到问题,请制作一个小示例并将其作为新问题发布(堆栈溢出喜欢每个问题都是独立的-它不是一个论坛,讨论可以转到其他内容)。
如果变量为:
在第二个C文件中,您声明:
尽量避免全球性的。如果必须使用全局变量,请参阅其他答案。
将它作为参数传递给函数。
- 那么,如何处理中断呢?假设触发了一个中断,我想读取一个pin上的值,那么在不将该值写入全局变量的情况下,如何返回该值呢?
- @RealityInabox——这是一个令人难以置信的特定领域的答案,而OP的问题更为广泛。在这种情况下,你很可能需要一个全球性的。(这是一个非常古老的答案,我省略了很多关于为什么线程化、可重入性、代码可读性的细节,以及为什么全局通常不是最佳选择。)
- @塔纳托斯,这不是什么"不可思议"的事情。除非您认为嵌入式开发的广阔世界是初学者永远不会询问的内容,否则您的答案过于狭隘和具体。有很多其他的原因,使用全球以外的东西,你显然认为是正常使用的C,如果有这样的事情。最后,在OP的问题中绝对没有上下文,所以你关于它的广泛性的陈述是一个假设。手术很好地询问了中断的情况。
- @RealityInabox请您详细说明为什么全局变量应该是通用的?
- @当人们说一些事情/应该/应该是某种方式而不说为什么的时候,我真的很恼火,这对任何阅读它的人来说都是毫无价值的信息,如果这个特性存在的话,那么它就是用来使用的。
99.9%的情况下,在文件之间共享非常量全局变量是不好的程序设计。当你真正需要这样做的时候,很少有案例:它们是如此的罕见,以至于我无法想出任何有效的案例。硬件寄存器的声明。
在大多数情况下,您应该使用(可能是内联的)setter/getter函数("public")、文件范围("private")中的静态变量,或者不完整的类型实现("private")。
在少数情况下,当需要在文件之间共享变量时,请执行以下操作:
1 2 3 4 5 6 7 8 9 10
| // file.h
extern int my_var;
// file.c
#include"file.h"
int my_var = something;
// main.c
#include"file.h"
use(my_var); |
不要将任何形式的变量定义放在H文件中。
- 我把这篇文章作为对这篇文章精确副本的回答。我觉得有些关于使用全局变量的建议是必要的,而不仅仅是盲目地向其他人传授不好的实践,而不首先提出警告。
- 你的文章过于固执己见,因为你夸大了一个全局变量的有效案例是多么罕见。在我看来,控制嵌入式系统中资源可用性的二进制信号量必须是全局的。共享资源的任何基于RTOS的固件都会充满全局信号量。同样地,当一个全球代表了一个真正应该在任何地方都可以使用的东西时,它可以在速度和发展方面变得更简单和更快。此外,单例变量基本上是具有某种句法糖分的全局变量。过于简单化?是的,但概念上是有效的。
- @Anthony在全局变量和文件范围变量之间存在差异,全局变量可以从程序中的任何位置访问(因此是全局变量),而文件范围变量是静态的,只能从声明它们的文件内部访问。除某些多线程场景外,后者并不是很糟糕的做法。注释中的示例都应该用文件范围变量实现。
- 因此,现在您试图说每个全局都应该是一个文件范围变量,这是不正确的,尤其是在嵌入式环境中,除非您认为所有代码都应该在一个文件中。你怎么知道你从未见过的代码中变量的范围呢?实现共享资源的RTOS任务是非常常见的,而且您永远不会将多个完全无关的任务放在同一个文件中。我希望看到一种没有全局信号灯的方法来控制对该资源的访问。这只是一个全球非常有用的例子,还有很多其他的例子。
- @Anthony私有封装的全部要点就是:您不应该看到或关心那些变量,在使用它们的文件之外。应该通过setter/getter函数(可能是内联的)来访问这些变量。属于一个特定模块的数据应该分配到这个模块中,如果需要的话,信号量的处理也很可能放在setter/getter函数中。信号量本身实际上就是这个原则的完美例子:您不知道或不需要知道它们是如何在内部处理的。
- @Anthony Globals在嵌入式世界中普遍使用的主要原因可能是许多嵌入式程序员都是电子技术人员,他们"溜进"了编程部门,缺乏关于正确程序设计的教育/知识,而不是使用Globals的任何技术优势。我专门从事嵌入式系统编程/程序设计,我记不起上次我使用(非常量)全局的时候了,除了全局寄存器映射文件中的硬件寄存器定义。在过去的10年里,我当然没有用过。
- 你所有的"可能"和"应该"和"不应该"的说法,同时打电话给任何使用全球经验不足和没有受过教育的人,都会把你的观点放在上下文中。很多人都不同意,很多"真正的"程序员也不同意。全局性可以降低代码复杂性,在高性能关键系统中,它们是绝对必须的。显然,您从来没有在一个高度受限的嵌入式系统上工作过,在这个系统中,几乎不可能有硬的截止日期,在那里根本没有时间从中断中调用函数。由于空间要求,内联也无效。
- 此外,如果您认为使用getter/setter可以神奇地解决全局问题,那么您显然误解了自己的观点。如果getter/setter对每个函数都可用,那么您所做的就是将问题置于函数调用之后。这里有很多人不同意你的一些讨论。1 2 2
- @安东尼,这都是80年代的论据。我们一直都有内联函数,它们已经标准化16年了。如果编译器正确地输入setter/getter函数,它将相当于直接读/写一个变量。如果没有,那么问题就出在编译器上。在极少数情况下,您会遇到非常困难的实时需求,正如我偶尔在工作中所做的那样,您将看到反汇编,并相应地调整代码。也许可以用汇编语言编写关键部分,在这种情况下,无论如何都不适用C实践。
- @此外,如果在另一个模块中编写超关键ISR,而不是在包含需要修改的变量的模块中编写,那么整个程序设计就是有缺陷的。"全球"辩论的归根结底是:正确地设计程序。整个"全球"的争论毫无意义,因为有些人将术语global定义为在函数外部声明的任何变量,而另一些人将其定义为一个全局可访问的变量。
其他变量必须被公开(使用Extn,public是C++),并且必须包含.c文件。但是,我建议创建适当的.h文件来定义所有变量。
例如,对于hello.c,您将拥有hello.h,而hello.h将存储变量定义。然后另一个.c文件,如world.c,会在顶部显示这段代码:
这将允许world.c使用hello.h中定义的变量
但比这要复杂一点。您可以使用<>包含在操作系统路径上找到的库文件。作为初学者,我将把您的所有文件都放在同一个文件夹中,并使用""语法。
第二个文件需要知道变量的存在。为此,您再次声明变量,但在变量前面使用关键字extern。这会告诉编译器变量是可用的,但在其他地方声明,从而防止声明它(同样,链接时会引起冲突)。虽然您可以将extern声明放在c文件本身中,但对于每个.c文件,通常都有一个附带的头文件(即.h文件),它为其他保存extern声明的文件提供函数或变量。这样可以避免复制extern声明,尤其是在多个其他文件中使用时。这同样适用于函数,尽管您不需要关键字extern来表示它们。
这样,您至少可以有三个文件:声明变量的源文件、执行extern声明的压缩头文件和#include作为头文件访问导出变量(或头中导出的任何其他符号)的第二个源文件。当然,当试图链接类似的内容时,您需要所有源文件(或适当的对象文件),因为链接器需要解析符号,只有当符号实际存在于链接的文件中时,才可能解析符号。