如何在不同的.c文件之间共享变量?

86
如何在一个.c文件中使用另一个.c文件中先前定义的变量?

12
我修改了你的标题。请尝试使标题更具描述性,以便1)能够回答这个问题的人可以轻松地看出你在问什么,2)其他有同样问题的人可以找到这个问题,这样他们就不必再次询问相同的问题。当查看问题列表时,我们不需要知道这是一个初学者问题,但我们确实需要知道这个问题是关于什么的。这只是一个未来提问的提示。:) - jalf
6个回答

169

在 fileA.c 文件中:

int myGlobal = 0;

在 fileA.h 文件中

extern int myGlobal;

在 fileB.c 文件中:

#include "fileA.h"
myGlobal = 1;

所以它的工作原理如下:

  • 变量存在于fileA.c文件中
  • fileA.h告诉世界它的存在,以及它的类型(int
  • fileB.c包含fileA.h,以便编译器在尝试使用它之前知道myGlobal的存在。

18
在非类上下文中,“static”意味着“限定于此翻译单元”。它基本上是“extern”的相反。你不能同时拥有两者。 - rmeador
2
@arbitter:没有头文件,你无法共享变量(除非你直接将“extern int myGlobal;”放入想要访问“myGlobal”的源文件中,但这是DRY原则的恶意违反)。如果您需要从main.c共享全局变量,请创建 main.h! - RichieHindle
@RichieHindle:不确定能否创建一个main.h文件,但我会将其放在另一个文件里。谢谢! - jdepypere
@RichieHindle:我又来了——在多个文档中使用多个char i;时遇到了麻烦。我在多个.c文件中声明了char i;,但没有在相应的.h文件中放置extern char i;,因为我希望每个文件都有一个i用于迭代目的,那么将其归属于某个特定的文件是否更好? - jdepypere
多个.c文件可以拥有自己的char i;,而不共享它们。这很正常。如果您在编译或运行现有内容时遇到问题,请制作一个小示例并将其发布为新问题(Stack Overflow希望每个问题都是自包含的 - 它不是一个论坛,讨论可以转移到其他事情上)。 - RichieHindle
显示剩余4条评论

15

在99.9%的情况下,共享非常量全局变量是不良的程序设计。实际上需要这样做的情况非常少,它们非常罕见,以至于我无法想出任何有效的情况,也许只有硬件寄存器的声明。

在大多数情况下,应该使用(可能是内联的)设置器/获取器函数(“public”)、文件作用域的静态变量(“private”)或不完整类型实现(“private”)。

在那些极少数需要在文件之间共享变量的情况下,请像这样做:

// file.h
extern int my_var;

// file.c
#include "file.h"
int my_var = something;

// main.c
#include "file.h"
use(my_var);

永远不要在头文件中放置任何形式的变量定义。


2
你的帖子过于主观,因为你夸大了全局变量有效用例的罕见程度。就我个人而言,嵌入式系统中管理资源可用性的二进制信号量必须是全局的。任何共享资源的基于RTOS的固件都会充斥着全局信号量。同样地,当全局变量代表真正应该在任何地方都可用的东西时,从速度和开发角度来看,它可能更简单、更快。此外,单例模式基本上是带有一些语法糖的全局变量。过于简化了吗?是的,但在概念上是有效的。 - Anthony
1
@Anthony,全局变量和文件作用域变量之间有区别。全局变量可以从程序的任何地方访问(因此是全局的),而文件作用域变量是静态的,只能从声明它们的文件内部访问。后者并不是真正的不良实践,除了在某些多线程场景下。你评论中的示例都应该使用文件作用域变量来实现。 - Lundin
1
现在你想说每个全局变量都应该是文件作用域变量,这是不正确的,特别是在嵌入式世界中,除非你认为所有代码都应该在一个文件中。你怎么知道你从未见过的代码中变量的作用域?实现共享资源的RTOS任务非常普遍,你永远不会将多个完全不相关的任务放在同一个文件中。我很想看到一种控制对该资源访问的方法,而不需要全局信号量。这只是全局变量极其有用的一个例子;还有许多其他例子。 - Anthony
@Anthony 私有封装的整个意义就在于此:你不应该在使用它们的文件之外看到或关心这些变量。对这些变量的访问应该通过设置器/获取器函数(可能是内联的)来完成。属于特定模块的数据应该分配在该模块中,并且如果需要处理信号量,则最有可能也可以放置在设置器/获取器函数中。信号量本身实际上就是这个原则的完美例子:你不知道也不需要知道它们如何在内部处理。 - Lundin
@Anthony 全局变量在嵌入式系统中通常被使用的主要原因可能是许多嵌入式程序员都是电子工程师,他们“滑”进了编程领域,缺乏有关正确程序设计的教育和知识,而不是因为使用全局变量有任何技术优势。我专门从事嵌入式系统编程/程序设计工作,我无法记起上次使用(非常量)全局变量是什么时候,除了在全局寄存器映射文件中使用硬件寄存器定义。过去10年来,我确实没有使用过这种变量。 - Lundin
显示剩余13条评论

13

如果变量为:

int foo;

在第二个C文件中你声明了:

extern int foo;

9
  1. 尽量避免使用全局变量。如果必须使用全局变量,请参考其他答案。
  2. 将其作为参数传递给函数。

7
那么你如何处理中断呢?比如触发了一个中断,并且想要读取引脚上的值,如何在不写入全局变量的情况下返回该值? - realityinabox
@realityinabox - 这是一个非常特定于领域的答案,而OP的问题则更加广泛。在这种情况下,您可能确实需要一个全局变量。(这是一个非常古老的答案,我省略了很多关于为什么-线程,可重入性,代码可读性-通常不是最佳选择的细节。) - Thanatos
7
@Thanatos,它并不是什么“难以置信”的东西。除非你认为嵌入式开发的广泛世界是初学者永远不会询问的内容,否则你的回答就过于狭窄和具体了。除了您明显认为的C语言的正常用法之外,还有许多其他使用全局变量的原因,如果有这样的东西的话。最后,OP的问题没有任何背景信息,所以你关于其范围过宽的说法是一种假设。OP很可能是在问中断方面的问题。 - Anthony
1
@realityinabox 你能否详细说明为什么应该在通用程序中避免使用全局变量? - Dávid Tóth
2
@Thanatos,当人们说某些事情“应该”是某种方式而不解释原因时,我真的很恼火,这对任何阅读此信息的人来说都是毫无价值的信息。如果该功能存在,则意味着它是用于使用的。 - Jose V

4

其他变量必须声明为public(使用extern,public是用于C ++的),并且您必须包含该.c文件。但我建议创建适当的.h文件来定义所有变量。

例如,对于hello.c,您将拥有一个hello.h,而hello.h将存储您的变量定义。然后,另一个.c文件(如world.c)将在顶部具有以下代码:

#include "hello.h"

这将允许world.c使用在hello.h中定义的变量。

但实际稍微有点复杂。你可以使用<>来包含在操作系统路径下找到的库文件。作为初学者,我建议将所有文件放在同一个文件夹中,并使用“”语法。


2
第二个文件需要知道变量的存在。为此,您需要再次声明该变量,但在其前面使用关键字extern。这告诉编译器该变量可用,但已在其他地方声明,从而防止实例化它(再次实例化会导致链接时发生冲突)。虽然您可以将extern声明放在C文件本身中,但通常的做法是为每个提供函数或变量给其他文件使用的.c文件编写相应的头文件(即.h文件),其中包含extern声明。这样,您就避免了复制extern声明,特别是如果它在多个其他文件中使用。对于函数也适用同样的方法,尽管对于它们不需要使用关键字extern

这样,您至少需要三个文件:声明变量的源文件、它的伴随头文件,执行extern声明的第二个源文件,该文件#include头文件以访问导出的变量(或在头文件中导出的任何其他符号)。当尝试链接此类内容时,当然需要所有源文件(或适当的目标文件),因为链接器需要解析符号,只有在链接的文件中实际存在该符号时才能完成解析。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接