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

C vs C++ global variable in header

本文关键字:全局变量 C++      更新时间:2023-10-16

我知道全局变量不应该在标头中定义,我们应该使用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("%dn", i);
add();
printf("%dn", i);
return 0;
}    
----
//lib.c
#include "lib.h"
void add(){
i++;
}

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

//main.cpp
#include <iostream>
#include "lib.h"
using namespace std;
int main()
{
cout<<i<<endl;
add();
cout<<i<<endl;
return 0;
}

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

这种行为差异不是巧合,也不是编译器中的错误。 这是对 C 和 C++ 标准的严格应用,它们在全球范围内拥有多个int i;的含义存在分歧。

在C++中它是无效的

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

这在C++ 标准 [basic.def.odr]一节中定义。

在 C 中它是有效的

在 C 语言中,int i;是一个暂定定义。 对完全相同的全局变量进行多个试探性声明是完全有效的。

这在 C11 标准第6.9.2节外部对象定义中定义:

/2:具有文件范围的对象的标识符声明 没有初始值设定项,也没有存储类说明符或 存储类说明符 static,构成暂定 定义。如果翻译单元包含一个或多个暂定 标识符的定义,并且翻译单元不包含 该标识符的外部定义,则行为恰好 好像翻译单元包含的文件范围声明 标识符,具有截至翻译末尾的复合类型 单位,初始值设定项等于 0。

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

这种中立是有原因的:

  • 该标准将这种情况标识为未定义的行为:

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

  • 但该标准还将具有多个定义的情况确定为广泛支持的常见扩展,只要这些定义不相互矛盾:

    附件J.5.11:一个物体的标识符可能有多个外部定义,无论是否明确使用 关键字extern;如果定义不一致,或者不止一个 已初始化,行为未定义

重要建议

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

当我使用类似的代码在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

不知道为什么它在 C 中工作,但在 C 和C++中都是错误的。尝试:

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

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