10

有资源(书籍,在线资料)解释了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'

我的问题是 - 这是CC++的区别,还是ANSIANSI之前的做法?

现在,更实际的问题:

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

extern MyClass myobject;

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

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

4

6 回答 6

12

特别是对于您的示例,这里的 C 和 C++ 之间没有区别。适用于两种语言的基本规则是:如果您的声明包含一个初始化程序,那么它就是一个定义。时期。没关系,它是否有明确extern的。如果它有一个初始化器,那么它就是一个定义

这意味着在命名空间范围内两者extern int i = 1int i = 1都是等价的,即extern在这样的声明中是多余的。在 C++extern中,当声明的对象是 时,定义变得非冗余const,因为constC++ 中的对象默认具有内部链接。例如,定义带有外部链接的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);

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

于 2012-08-08T16:46:43.480 回答
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;定义编写。

于 2012-08-08T15:42:43.173 回答
3

草案 n3337, 3.1.2

声明是一个定义,除非它声明一个函数而不指定函数的主体 (8.4),它包含 extern 说明符 (7.1.1)或链接规范25 (7.5)并且既不包含初始值设定项 也不是函数体,它在类定义(9.2、9.4)中声明一个静态数据成员,它是一个类名声明(9.1),它是一个不透明枚举声明(7.2),它是一个模板参数(14.1 ),它是函数声明器中的参数声明 (8.3.5),它不是函数定义的声明器,或者它是 typedef 声明 (7.1.3),别名声明 (7.1.3), using-declaration (7.3.3)、static_assert-declaration (Clause 7)、attributeedeclaration (Clause 7)、empty-declaration (Clause 7) 或 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

—结束示例]

于 2012-08-08T15:34:28.113 回答
2

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

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

你应该做的是:

extern MyClass myobject;

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

MyClass myobject;

这是一个带有外部链接的定义。

于 2012-08-08T15:42:53.240 回答
2

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

所以完整的例子看起来像这样:

----MyMath.h----

#ifndef MYMATH_H_INCLUDED
#define MYMATH_H_INCLUDED

extern float PI;

#endif // MYMATH_H_INCLUDED

----MyMath.cpp----

#include "MyMath.h"

float PI = 3.1415;

----main.cpp----

#include <iostream>
#include "MyMath.h"

using namespace std;

int main()
{
    cout << "PI = " << PI << endl;
    return 0;
}

希望我解释清楚!请记住,这是为了在文件之间使用变量!

于 2014-06-15T21:21:42.570 回答
0

“初始化程序”胜过“外部”。行动胜过谈话。

于 2013-08-07T19:34:43.127 回答