Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/joomla/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 头文件中的全局变量_C - Fatal编程技术网

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);

我有两个模块(.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);
    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种情况,您可以描述:

  • 具有2个
    .c
    文件和
    inti在标题中
  • 具有2个
    .c
    文件和
    int i=100(或任何其他值;这无关紧要)
  • 使用1
    .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();