我正在尝试理解在多个文件(编译单元)之间共享C全局变量的方式。我已经阅读了优秀的问题和答案here。然而,在做了一些测试后,我仍然留下了一些我没有得到的东西:
基本上我的问题是:如果在没有extern
关键字的标题中声明了(但未定义)变量,是否可以简单地将该标题包含在各种编译单元中以使该变量可用所有那些编译单位?在这种情况下,暗示一个(且只有一个)编译单元包含用于初始化(定义?)该变量的代码,并且在其他编译单元尝试对该变量执行任何操作之前将首先调用该代码。如果这一切都是真的,那么这个程序被称为“隐式外部”吗?
我将用一个例子说明我的问题:
标题“MyCommonHeader.h”包含:
//MyCommonHeader.h
int* i; //pointer to an int
文件MyFirstHeader.h包含:
//MyFirstHeader.h
void changeIt(int newValue);
文件MyFirstSource.c包含:
//MyFirstSource.c
#include "MyFirstHeader.h"
void changeIt(int newValue) {
*i = newValue;
}
文件MySecondSource.c包含:
//MySecondSource.c
#include "MyCommonHeader.h"
#include "MyFirstHeader.h"
void main() {
i = malloc(sizeof(int));
changeIt(10);
*i = 23;
}
以上代码是否在所有地方都使用相同的i变量?我需要在任何地方添加extern
吗?
答案 0 :(得分:3)
/* file.h */
int* i;
是i
变量的暂定定义。这意味着如果翻译单元中没有该变量的其他(外部)定义,则只定义一次(初始化为0
)。如果翻译单元中的其他地方只有一个匹配(外部)定义i
,则将使用该定义,并且上面的暂定定义将表现为声明。
作为一种常见的扩展,编译器会跨翻译单元扩展此行为。这意味着,对于此类编译器,您可以安全地将该头文件包含在您想要的任意数量的翻译单元中,并且仍然只有i
的一个定义。
如果您还在头文件中明确初始化了i
,那将会有所不同:
/* file.h */
int* i = 0;
这是一个实际的定义(不是暂定的),你只能在一个编译单元中包含该头文件,否则你会得到一个多重定义错误。
更好的方法是在.c文件中定义变量,并在头文件中使用extern
:
/* file.h */
extern int* i;
/* file.c */
int* i = 0;
这清楚地表明只有一个定义(.c文件中的定义),并且包含头文件的每个编译单元都将引用该定义。
答案 1 :(得分:1)
如果在标题中声明变量并且任何源文件也声明了同一个变量,则每个编译单元都将具有此变量的不同实例。
在一个单独的编译单元中声明它,并在标题中添加“extern ...”将使所有编译单元访问相同的全局变量。
答案 2 :(得分:0)
Does the above code operates with the same i variable everywhere?
是。
Do I need to add externanywhere?
在此示例中,不需要。因为MyCommonHeader.h
仅在我定义的地方包含一次。
在以下示例中,extern
更有用。
//main.c
#include <stdio.h>
int globaldata;
int main()
{
..
}
//Includes.h
extern int globaldata;
//Feature1.c
#include "Includes.h"
int func()
{
globaldata++;
}
//Feature2.c
#include "Includes.h"
int func_new()
{
globaldata = globaldata * 100;
}
globaldata
是feature1.c和feature2.c使用的全局变量。如果您定义该变量,那么它将是错误,如多个声明错误。
因此在此scnario中始终使用extern,并且仅包含该标题。
答案 3 :(得分:0)
在一个头文件中声明然后包含在多个地方,你最终会得到i的多个实例(并且可能会编译或链接问题)。所以在头文件中使用extern并在一个文件中定义它是一个更好的主意.c文件。在此之后,您可以包含标题而不重复变量定义。