这个问题在这里已有答案:
我知道全局变量不应该在头文件中定义,我们应该使用extern
来仅在头文件中声明它们。
但是我已经尝试在以下标题lib.h
中定义一个全局变量:
//lib.h
int i;
void add();
尝试在C和C ++中使用此标头时,我得到了一些有趣的结果
在C中,我在main.c
和lib.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.h
和lib.cpp
与上面相同),它会给出关于具有多个定义的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 ++中使用类似代码运行它时,它会给出一个关于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;
}
这种行为差异并非巧合,也不是编译器中的错误。这是C和C ++标准的严格应用,它们在全球范围内有几个int i;
的含义不同。
在C ++中,int i;
是(未初始化的)对象的定义。 One Definition Rule (ODR)不允许您定义多个时间相同的全局变量。
这在C ++标准的[basic.def.odr]部分中定义。
在C中,int i;
是一个暂定的定义。拥有完全相同的全局变量的几个临时声明是完全有效的。
这在C11标准中定义,第6.9.2节外部对象定义:
/ 2:具有文件范围而没有初始化程序且没有存储类说明符或存储类说明符为静态的对象的标识符声明构成暂定定义。如果翻译单元包含一个或多个标识符的暂定定义,并且翻译单元不包含该标识符的外部定义,那么行为就像翻译单元包含该标识符的文件范围声明一样,复合类型为翻译单元的结尾,初始化程序等于0。
请注意,该子句的措辞方式没有说明在多个翻译单元中定义相同变量的情况。上面标准引用的最后一句并不意味着它是每个文件中的不同变量(为此你需要内部链接,使用static
)。它只是表示行为就好像变量的初始值为0。
这种中立有一个原因:
extern
;如果定义不一致,或者初始化多个,则行为未定义重要的建议
因此,如果您打算编写可移植代码,我强烈建议在标头中使用extern
,并在一个且仅在一个编译单元中定义该值。这是安全,清晰,明确的,并且适用于C语言和C ++。
所以,这是关于预处理器的棘手问题:当你使用#define时它会复制和粘贴。这意味着main.cpp看到的int i;
与lib.cpp看到的int i;
不同。