标头中的C与C ++全局变量

时间:2019-03-28 18:01:30

标签: c++ c global-variables language-lawyer cross-language

我知道不应在标头中定义全局变量,而应该使用extern仅在标头中声明它们。

尽管如此,我仍然尝试在以下标头 lib.h 中定义全局变量:

//lib.h

int i;

void add();

尝试在C和C ++中使用此标头时,我得到了一些有趣的结果

在C语言中,我将标头包含在main.clib.c中,并且它可以编译并正常运行:

//main.c

#include <stdio.h>
#include <stdlib.h>
#include "lib.h"

int main()
{
    printf("%d\n", i);
    add();
    printf("%d\n", i);
    return 0;
}    

----
//lib.c

#include "lib.h"

void add(){
    i++;
}

但是,当我使用类似的代码在C ++中运行它时(lib.hlib.cpp与上面的代码相同),它会给出有关具有多个定义的i变量的错误消息: / p>

//main.cpp

#include <iostream>
#include "lib.h"
using namespace std;

int main()
{
    cout<<i<<endl;
    add();
    cout<<i<<endl;
    return 0;
}

为什么它用C而不是C ++编译?

4 个答案:

答案 0 :(得分:1)

  

当我使用类似的代码在C ++中运行它时,会给出有关具有多个定义的i变量的错误消息。为什么会这样?

C ++标准说:

  

[basic.def.odr]每个程序都应在该程序之外的每个非内联函数或变量中包含恰好一个定义。废弃的陈述;无需诊断。

lib.cpp(我假设这是您在c ++中的“类似”源文件)和main.cpp都定义了全局变量int i。因此,该程序格式不正确。

解决方案:仅在标头中声明变量。精确定义一个翻译单位:

//lib.h
extern int i; // this declaration is not a definition

//lib.cpp
int i;        // this declaration is     a definition

答案 1 :(得分:0)

不确定为什么它可以在C中运行,但是在C和C ++中都是错误的。试试:

// lib.h
extern int i;
void add();
// lib.c or lib.cpp
#include "lib.h"
int i = 0;
void add()
{
  ++i;
}

答案 2 :(得分:0)

这种行为差异不是巧合,也不是编译器中的错误。这是C和C ++标准的严格应用,其区别在于在全局范围内具有多个int i;的含义。

在C ++中无效

在C ++中,int i;是(未初始化的)对象的定义。 One Definition Rule (ODR) 不允许您多次定义同一全局变量。

这在C ++标准的 [basic.def.odr]

部分中定义

在C语言中有效

在C中,int i; 临时定义 。具有几个完全相同的全局变量的临时声明是完全有效的。

这在C11标准的 6.9.2外部对象定义部分中定义:

  

/ 2::声明具有文件范围的对象的标识符   没有初始化程序,没有存储类说明符,或者没有   存储类说明符是静态的,构成一个暂定   定义。如果翻译单元包含一个或多个暂定词   标识符的定义,翻译单元不包含   标识符的外部定义,那么行为就是   好像翻译单元包含该文件的文件范围声明   标识符,复合类型截至翻译结束   单位,初始值设定为0。

请注意,此子句的措词没有说明在多个翻译单元中定义相同变量的情况。上面的标准引号的最后一句话并不意味着每个文件中的变量都是不同的(为此,您需要使用static进行内部链接)。只是说行为就像变量的初始值为0。

这种中立有一个原因:

  • 标准将案例识别为未定义行为:

      

    附件J.2:使用具有外部链接的标识符,但是在程序中   不完全存在标识符的一个外部定义,或者   标识符未使用,并且存在多个外部   标识符的定义

  • 但是该标准还将带有多个定义的情况标识为广泛支持的通用扩展,只要这些定义彼此不矛盾即可:

      

    附件J.5.11::无论是否明确使用对象标识符,对象的标识符可能有多个外部定义。   关键字extern;如果定义不同意,或者多个   初始化后,行为未定义

重要建议

由于这个原因,如果您打算编写可移植的代码,强烈建议您在标头中使用extern并在一个编译单元中(仅一个)中定义值。这是安全,清晰,明确的,并且可以在C和C ++中使用。

答案 3 :(得分:-2)

因此,这是关于预处理器的棘手的事情:使用#define时,它会复制并粘贴。这意味着main.cpp看到的int i;与lib.cpp看到的int i;不同。