正确使用'extern'关键字

时间:2012-08-08 15:02:30

标签: c++ extern

有些来源(书籍,在线资料)解释了extern的用法如下:

extern int i;        // declaration - has 'extern'
int i = 1;           // definition  - specified by the absence of 'extern'

有些来源支持以下语法:

extern int i;        // declaration
extern int i = 1;    // definition  - specified by the equal sign
                     // Both marked with 'extern'

我的问题是 - 这是 C C ++ 的区别,还是 pre-ANSI ANSI 练习?

现在,更实际的问题是:

使用第二种语法,我想创建一个全局对象(从每个编译单元可见)。构造函数不带参数,因此不需要括号,也不需要等号。

extern MyClass myobject;

现在,编译器如何区分声明和定义?

编辑:回到学校,我习惯了第一种语法(Borland C)。后来我使用了一个编译器(可能是GCC的一些古老版本)拒绝编译没有'extern'的定义。这让我很困惑。

6 个答案:

答案 0 :(得分:9)

特别是对于您的示例,此处的C和C ++之间没有区别。适用于这两种语言的基本规则是:如果您的声明包含初始值设定项,则它是定义。期。无论是否有明确的extern都没关系。如果它有初始值设定项,则它是定义

这意味着在命名空间范围内,extern int i = 1int i = 1都是等效的,即此类声明中的extern是多余的。在C ++ extern中,当声明的对象为const时,定义变为非冗余,因为C ++中的const对象默认具有内部链接。例如,extern const int c = 42;使用外部链接定义常量c

如果声明没有初始值设定项,那么(并且只有这样)它才开始依赖于extern关键字的存在。使用extern,它是非定义声明。没有extern,它就是定义。 (在C中它将是暂定的定义,但这与我们上下文中的点不同。)

现在,针对您的实际问题。要创建全局对象,您必须声明为

extern MyClass myobject;

(通常在头文件中完成),然后在某些翻译单元中定义

MyClass myobject;

由于构造函数不带参数,因此这是定义对象的唯一方法。 (从C ++ 11开始,如果您愿意,也可以使用MyClass myobject{};。)

如果你必须为构造函数提供参数(例如,42),你就可以使用它们

MyClass myobject(42);

extern MyClass myobject(42);

作为定义,因为初始化程序的存在确保它确实被解释为定义

答案 1 :(得分:4)

对于文件范围变量,无论它们是类类型还是基本类型:

    没有初始化的
  • extern T t;是声明;
  • extern T t = expression;带有任何语法的初始化(赋值,构造或统一)是一个定义;
  • 没有初始化工具的
  • T t;是一个定义,初始化为默认值T;
  • T t = expression;使用任何语法的初始化程序定义。

extern int i = 1;int i = 1;之间没有区别,并且两种风格都有争论,但总的来说我会争论第二种,因为你应该已经知道了这个定义在文件范围内有链接。

从历史上看,似乎在ANSI C之前不需要extern关键字;见例如http://www.jetcafe.org/jim/c-style.html#Declarations

因此,对于类类型,请为声明编写extern MyClass myobject;,为定义编写MyClass myobject;

答案 2 :(得分:3)

草案n3337,3.1.2

  

声明是一个定义,除非声明没有的函数   指定函数的主体(8.4),它包含extern说明符   (7.1.1)或者链接规范25(7.5)并且都没有初始化程序   也不是函数体,它在类中声明了静态数据成员   定义(9.2,9.4),它是一个类名声明(9.1),它是一个   opaque-enum-declaration(7.2),它是一个模板参数(14.1),它   是函数声明符中的参数声明(8.3.5)   不是函数定义的声明者,或者是typedef   声明(7.1.3),别名声明(7.1.3),使用声明   (7.3.3),static_assert-declaration(第7条),a   attributedeclaration(第7条),空声明(第7条)或a   using-directive(7.3.4)。 [示例:以下所有内容均为   定义:

int a; // defines a
extern const int c = 1; // defines c
int f(int x) { return x+a; } // defines f and defines x
struct S { int a; int b; }; // defines S, S::a, and S::b
struct X { // defines X
int x; // defines non-static data member x
static int y; // declares static data member y
X(): x(0) { } // defines a constructor of X
};
int X::y = 1; // defines X::y
enum { up, down }; // defines up and down
namespace N { int d; } // defines N and N::d
namespace N1 = N; // defines N1
X anX; // defines anX
whereas these are just declarations:
extern int a; // declares a
extern const int c; // declares c
int f(int); // declares f
struct S; // declares S
typedef int Int; // declares Int
extern X anotherX; // declares anotherX
using N::d; // declares d
  

-end example]

答案 3 :(得分:2)

  

我想创建一个全局对象(从每个编译单元可见)

extern对于定义不是必需的,因为外部链接是默认的。

你应该做的是:

extern MyClass myobject;

在头文件中(这不是定义)使编译器在编译其他编译单元时知道数据类型。然后,在一个编译单元中,写下:

MyClass myobject;

这是一个外部联系的定义。

答案 4 :(得分:2)

好的,所以extern关键字放在外部文件中变量的名称之前。所以说你的项目中有一个单独的文件。假设该文件是名为MyMath.h的头文件(就好像您正在制作带有一些有用函数/类的可移植数学文件)。在您的头文件中,您可以为所有酷的数学函数和类放置所有原型或​​前向引用。此数学头文件的实际代码或函数等将位于名为MyMath.cpp的.cpp文件中(通常使用相同的名称将其全部组织起来)。 extern关键字在这里发挥作用:如果你想在数学文件中有PI(3.1415)的全局变量,你需要像往常一样定义它(在.cpp文件中),{{1在你的.h或头文件中你可以编写原型或变量声明,前缀为float PI = 3.1415;

所以完整的例子可能如下所示:

---- ---- MyMath.h

extern

---- ---- MyMath.cpp

#ifndef MYMATH_H_INCLUDED
#define MYMATH_H_INCLUDED

extern float PI;

#endif // MYMATH_H_INCLUDED

---- ---- main.cpp中

#include "MyMath.h"

float PI = 3.1415;

希望我彻底解释!请记住,这是变量'在文件之间使用!

答案 5 :(得分:0)

“初始化程序”击败“extern”。行动节拍。

相关问题