C 头文件中的全局变量
我有两个模块(.c文件)和一个.h头文件: 文件1.c:C 头文件中的全局变量,c,C,我有两个模块(.c文件)和一个.h头文件: 文件1.c: #include <stdio.h> #include "global.h" int main() { i = 100; printf("%d\n",i); foo(); return 0; } #include <stdio.h> #define DEFINE_I #include "global.h" int main() { printf("%d\n",i);
#include <stdio.h>
#include "global.h"
int main()
{
i = 100;
printf("%d\n",i);
foo();
return 0;
}
#include <stdio.h>
#define DEFINE_I
#include "global.h"
int main()
{
printf("%d\n",i);
foo();
return 0;
}
当我使用gcc file1.c file2.c时,一切都很好,我得到了预期的输出。现在,当我在头文件中将变量“I”初始化为0并再次编译时,我得到一个链接器错误:
/tmp/cc0oj7yA.o:(.bss+0x0): multiple definition of `i'
/tmp/cckd7TTI.o:(.bss+0x0): first defined here
如果我只是编译file1.c(删除对foo()的调用),并在头文件(即gcc file1.c)中进行初始化,那么一切都可以正常工作。发生了什么事?不要在标题中初始化变量。将声明放在标题中,并将初始化放在一个
c
文件中
在标题中:
extern int i;
在文件2.c中:
int i=1;
您不应该在头文件中定义全局变量。您可以在头文件中将它们声明为
extern
,并在.c
源文件中定义它们
(注意:在C中,
int i;
是一个暂定定义,如果在转换单元中找不到该变量的其他定义,它会为该变量分配存储(=是一个定义)。有3种情况,您可以描述:
.c
文件和inti代码>在标题中
.c
文件和int i=100标题中的code>(或任何其他值;这无关紧要)
.c
文件和int i=100代码>在标题中
.c
文件中,这个.c
文件编译成.o
文件,然后这些文件链接在一起
然后发生以下情况:
.o
文件都包含其中一个定义,因此链接器会说“ok”.o
文件都包含一个具有值的定义,这会发生冲突(即使它们具有相同的值)-在给定时间链接在一起的所有.o
文件中,可能只有一个具有任何给定名称.o
文件,所以不可能发生冲突- 将
extern inti代码>或只是
inti将>编码到头文件中
- 然后将i的“真实”定义(即
)放入inti=100;
。在这种情况下,此初始化在程序开始时使用,可以省略file1.c
中的相应行。(此外,我希望命名只是一个示例;在实际程序中,请不要将任何全局变量命名为main()
。)I
这种类型的代码在移植时会产生问题 @glglglgl已经解释了为什么您试图做的事情不起作用。实际上,如果您的目标是在标头中定义变量,则可以使用一些预处理器指令: 文件1.c:
#include <stdio.h>
#include "global.h"
int main()
{
i = 100;
printf("%d\n",i);
foo();
return 0;
}
#include <stdio.h>
#define DEFINE_I
#include "global.h"
int main()
{
printf("%d\n",i);
foo();
return 0;
}
在这种情况下,i
仅在您定义DEFINE_i的编译单元中定义,并在其他地方声明。链接器没有抱怨
我以前见过几次,在头中声明了枚举,下面是包含相应标签的char**的定义。我确实理解作者为什么喜欢将该定义放在头文件中,而不是放在特定的源文件中,但我不确定实现是否如此优雅 这个问题的答案是错误的。C116.9.2/2:
如果翻译单元包含一个或多个暂定定义
标识符,并且翻译单元不包含该标识符的外部定义,则该行为完全类似于翻译单元包含该标识符的文件范围声明,复合类型在翻译单元末尾,初始值设定项等于0
因此,问题中的原始代码的行为就好像file1.c
和file2.c
都包含inti=0行代码>结尾,由于多个外部定义(6.9/5),导致未定义的行为
因为这是一个语义规则而不是约束,所以不需要诊断
以下是关于同一代码的另外两个问题以及正确答案:
请注意,OP并没有初始化变量,这是一个暂定的定义。@Banthar:为什么它会在第二种情况下工作(当我刚刚编译file1.c时)?@Bruce:因为在这种情况下,它只初始化一次。我所说的“将extern int I;
或int I;
放入头文件”:extern int i
更好,因为它会立即告诉您是否由于某种意外而丢失了“真实”定义。如果只使用int i
,那么0将有一个无声的定义。我没有得到第一个解释,您可以详细解释一下内存分配给变量i的时间吗。到目前为止,我所理解的是全局的int i,h相当于extern int i;这意味着两个对象文件都引用了分配给其他地方的内存。@user2383973该内存由链接器分配和保留。当它看到一个带有赋值的请求和一个“暂定”定义时,一切都很好。当它看到多个赋值时,即使它们具有相同的值,也不正常。@FoadRezek我想您尝试了问题中的文件结构。这是因为分配在文件级别无效,仅在函数内部有效。如果1是未定义的行为,则暂定定义会导致为其出现的每个翻译单元生成外部定义。(C116.9.2/2)。您没有extern
的推荐也有同样的问题您想做什么say@tod. 不是最好的回答,但如果你想
#include <stdio.h>
#include "global.h"
void foo()
{
i = 54;
printf("%d\n",i);
}
#ifdef DEFINE_I
int i = 42;
#else
extern int i;
#endif
void foo();