头文件中的全局变量

Bru*_*uce 50 c

我有一个2模块(.c文件)和一个.h头文件:

file1.c中:

#include <stdio.h>
#include "global.h"

int main()
{
    i = 100;
    printf("%d\n",i);
    foo();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

file2.c中

#include <stdio.h>
#include "global.h"

void foo()
{
    i = 10;
    printf("%d\n",i);
}
Run Code Online (Sandbox Code Playgroud)

global.h

int i;
extern void foo()
Run Code Online (Sandbox Code Playgroud)

当我做gcc file1.c file2.c一切正常,我得到了预期的输出.现在,当我将头文件中的变量'i'初始化为0并再次编译时,我得到一个链接器错误:

/tmp/cc0oj7yA.o:(.bss+0x0): multiple definition of `i'
/tmp/cckd7TTI.o:(.bss+0x0): first defined here
Run Code Online (Sandbox Code Playgroud)

如果我只是通过头文件中的初始化(即gcc file1.c)编译file1.c(删除对foo()的调用),一切正常.到底是怎么回事?

glg*_*lgl 46

有三种情况,您描述:

  1. 有两个.c文件,并int i;在标题中.
  2. 使用2个.c文件并int i=100;在标题中(或任何其他值;这无关紧要).
  3. 有1个.c文件并int i=100;在标题中.

在每个场景中,想象插入到.c文件中的头文件的内容,并将此.c文件编译成.o文件,然后将这些文件链接在一起.

然后发生以下情况

  1. 由于已经提到的"暂定定义" .o,所以工作正常:每个文件都包含其中一个,因此链接器显示"ok".

  2. 不起作用,因为两个.o文件都包含一个带有值的定义,它会发生冲突(即使它们具有相同的值) - .o在给定时间链接在一起的所有文件中可能只有一个具有任何给定名称的名称.

  3. 当然,因为你只有一个.o文件,所以不可能发生碰撞.

恕我直言,干净的事情会是

  • 将任一个extern int i;或只是int i;放入头文件,
  • 然后将i(即int i = 100;)的"真实"定义放入file1.c.在这种情况下,该初始化在程序开始时使用,并且main()可以省略相应的行.(此外,我希望命名只是一个例子;请不要像i在实际程序中那样命名任何全局变量.)

  • 我所说的"把`extern int i;`或者只是`int i;`放入头文件中":`extern int i`更好,因为它会立即告诉你某些意外是否遗漏了"真正的"定义.仅仅是`int i`,就会有一个静默的定义为'0`. (8认同)
  • @user2383973 内存由链接器分配和保留。当它看到一个带有分配的请求和一个“暂定”定义时,一切都很好。当它看到多个赋值时,即使它们具有相同的值,也是不行的。 (2认同)
  • 情况 1 是未定义的行为,暂定定义会导致为其出现在 中的每个翻译单元生成外部定义。(C11 6.9.2/2)。您没有“extern”的建议也有同样的问题 (2认同)

Pio*_*zmo 33

不要在标头中初始化变量.将声明放在标题中并在其中一个c文件中初始化.

在标题中:

extern int i;
Run Code Online (Sandbox Code Playgroud)

在file2.c中:

int i=1;
Run Code Online (Sandbox Code Playgroud)


nin*_*alj 9

您不应在头文件中定义全局变量.您可以将它们声明为extern头文件,并在.c源文件中定义它们.

(注意:在C中,它int i;是一个暂定的定义,如果找不到该变量的其他定义,它会为变量分配存储空间(=是一个定义).)

  • 另见:http://ninjalj.blogspot.com/2011/10/tentative-definitions-in-c.html (5认同)
  • 如果我将 #ifndef 放在标题中并在标题中声明变量 `int a` 会怎样?尽管现在我只有一份头文件副本,但仍会出现多重定义错误。 (2认同)

M.M*_*M.M 8

目前对此问题接受的答案是错误的。C11 6.9.2/2:

如果翻译单元包含标识符的一个或多个临时定义,并且翻译单元不包含该标识符的外部定义,则行为就像翻译单元包含该标识符的文件范围声明一样,复合类型为翻译单元末尾的位置,初始值设定项等于 0。

因此,问题中的原始代码的行为就好像file1.c每个都在末尾file2.c包含该行,这会由于多个外部定义而导致未定义的行为(6.9/5)。int i = 0;

由于这是语义规则而不是约束,因此不需要诊断。

以下是关于相同代码的另外两个问题以及正确答案: