声明引入标识符并描述其类型,无论是类型、对象还是函数。声明是编译器接受对该标识符引用所需的内容。以下是声明:
extern int bar;
extern int g(int, int);
double f(int, double); // extern can be omitted for function declarations
class foo; // no extern allowed for type declarations
定义实际上是将此标识符实例化/实现。这是链接器需要的内容,以便将对这些实体的引用链接起来。这些是与上述声明相对应的定义:
int bar;
int g(int lhs, int rhs) {return lhs*rhs;}
double f(int i, double d) {return i+d;}
class foo {};
定义可以替代声明。
标识符可以被声明任意多次。因此,以下在 C 和 C++ 中是合法的:
double f(int, double);
double f(int, double);
extern double f(int, double); // the same as the two above
extern double f(int, double);
然而,它必须被定义一次。如果您忘记为已声明和引用的内容定义,则链接器不知道要链接到哪个引用,并抱怨缺少符号。如果您定义了多次某些东西,则链接器不知道要将引用链接到哪个定义,并抱怨有重复的符号。
由于在C++中什么是类声明与类定义的争论一直存在(在回答和评论其他问题时),我在这里摘录了C++标准的一句话。
在C++03的3.1/2处,它说:
除非[...]是类名声明[...],否则声明就是定义。
然后,3.1/3给出了一些例子。其中:
[Example: [...] struct S { int a; int b; }; // 定义了S、S::a和S::b [...] struct S; // 声明S —end example
总之:C++标准认为struct x;
是一个声明,struct x {};
是一个定义。(换句话说,“前向声明”是一个错误的名称,因为在C++中没有其他形式的类声明。)
感谢litb(Johannes Schaub)在他的一个回答中挖掘出了实际的章节和篇章。
extern int i
是一个声明,因为它只是引入或指定了变量 i
。在每个编译单元中,你可以有尽可能多的 extern int i
。 然而,int i
是一个定义。它表示这个翻译单元中整数所占用的空间,并通知链接器将所有对 i
的引用链接到这个实体上。如果你有多于或少于一个这样的定义,链接器就会报错。 - sbiint i;
都是定义。在C中,这是因为它分配了存储空间,在C++中也一样,因为它没有extern说明符或链接规范。这两种情况实际上是相同的,正如sbi所说:在这两种情况下,此声明指定了该作用域内所有对“i”的引用必须链接到的对象。 - Steve Jessopstruct A { double f(int, double); double f(int, double); };
当然是无效的。但其他地方是允许的。有些情况下可以声明,但不能定义:void f() { void g(); }
是有效的,但以下不是:void f() { void g() { } };
在涉及模板时,定义和声明有微妙的规则-请注意!对于好的回答加1。 - Johannes Schaub - litb根据C++标准第3.1节:
声明将名称引入翻译单元或重新声明由先前声明引入的名称。声明指定这些名称的解释和属性。
下一个段落指出(强调是我的),除非声明一个函数而不指定函数体,否则声明就是定义:
void sqrt(double); // declares sqrt
在类定义中声明静态成员:
struct X
{
int a; // defines a
static int b; // declares b
};
它声明了一个类名:
class Y;
...它包含了没有初始化器或函数体的extern
关键字:
extern const int i = 0; // defines i
extern int j; // declares j
extern "C"
{
void foo(); // declares foo
}
...或者是typedef
或using
语句。
typedef long LONG_32; // declares LONG_32
using namespace std; // declares std
了解声明和定义的区别之所以重要,有一个非常重要的原因:C++中的一次定义规则(One Definition Rule)。引用C++标准3.2.1节:
任何翻译单元都不得包含多于一个任何变量、函数、类类型、枚举类型或模板的定义。
struct x {static int b = 3; };
吗? - RJFalconerb
也被声明为 const
。请参见 https://dev59.com/U3A65IYBdhLWcg3w-jym#3536513 和 http://www.daniweb.com/software-development/cpp/threads/140739/taking-an-address。 - Kyle Strand声明: "某个地方存在一个foo。"
定义: "...在这里它就是!"
在C++(也有一些在C中)中存在一些有趣的边缘情况。请考虑:
T t;
这可以是定义或声明,具体取决于T
的类型:
typedef void T();
T t; // declaration of function "t"
struct X {
T t; // declaration of function "t".
};
typedef int T;
T t; // definition of object "t".
在C++中,当使用模板时,还存在另一种特殊情况。
template <typename T>
struct X {
static int member; // declaration
};
template<typename T>
int X<T>::member; // definition
template<>
int X<bool>::member; // declaration!
上一个声明并不是定义。这是对 X<bool>
的静态成员进行显式特化的声明。它告诉编译器:“如果要实例化 X<bool>::member
,则不要从主模板实例化成员的定义,而是使用在其他地方找到的定义。” 要将其定义,必须提供初始化程序。
template<>
int X<bool>::member = 1; // definition, belongs into a .cpp file.
声明
声明告诉编译器程序元素或名称的存在。一个声明会将一个或多个名称引入程序中。在程序中,声明可以出现多次。因此,每个编译单元都可以为类、结构体、枚举类型和其他用户定义的类型进行声明。
定义
定义指明名称描述的代码或数据。在使用名称之前必须先进行声明。
class foo {};
就是一个类的定义,对吧? - sbi来自C99标准的第6.7(5)条:
一个声明指明了一组标识符的解释和属性。对于一个标识符的定义是指这个标识符的声明:
来自C++标准的3.1(2)条:
除非声明一个没有指定函数体的函数、包含extern修饰符或链接说明符并且没有初始化程序或函数体、在类声明中声明一个静态数据成员、是一个类名声明、typedef声明、using声明或using指令,否则声明就是一个定义。
然后有一些例子。
有趣的是(或者不是,但我稍微感到惊讶),typedef int myint;
在C99中是一个定义,但在C++中只是一个声明。
来自wiki.answers.com:
在C语言中,“声明”表示你正在告诉编译器有关程序中任何变量、用户定义类型或函数的类型、大小以及在函数声明的情况下,其参数类型和大小。在声明时,并没有为变量保留内存空间。然而,如果创建了这种类型的变量,编译器会知道要保留多少空间。
例如,以下都是声明:
extern int a;
struct _tagExample { int a; int b; };
int myFunc (int a, int b);
与声明相反,定义意味着除了所有声明的内容外,还会在内存中保留空间。可以说“定义=声明+空间预留”。以下是定义的示例:
int a;
int b = 0;
int myFunc (int a, int b) { return a + b; }
struct _tagExample example;
请查看Answers。
struct foo {};
是一种定义,而不是声明。foo
的声明应该是 struct foo;
。编译器从中无法知道为foo
对象预留多少空间。 - sbistruct foo;
是一个声明,但它并没有告诉编译器foo的大小。我想补充说明的是struct _tagExample { int a; int b; };
是一个定义。因此,在这种情况下称其为声明是具有误导性的。当然,它确实是一个声明,因为所有定义都是声明,但你似乎在暗示它不是一个定义,它是_tagExample的定义。 - Steve Jessop:^>
) - sbi由于我没有看到与C ++ 11相关的答案,这里有一个。
除非它声明了以下内容,否则声明就是定义:
enum X:int;
template<typename T> class MyArray;
中的Tint add(int x, int y);
中的x和yusing IntVector = std :: vector<int>;
static_assert(sizeof(int) == 4, "Yikes!")
;
从上述列表继承的其他条款:
int add(int x, int y);
中的addextern int a;
或extern"C"{...};
class C { static int x; };
中的xstruct Point;
typedef int Int;
using std :: cout;
using namespace NS;
模板声明是一个声明。如果其声明定义了函数、类或静态数据成员,则模板声明也是定义。
我在理解它们之间微妙差别方面有帮助的标准示例:
// except one all these are definitions
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
// all these are 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 N::d
// specific to C++11 - these are not from the standard
enum X : int; // declares X with int as the underlying type
using IntVector = std::vector<int>; // declares IntVector as an alias to std::vector<int>
static_assert(X::y == 1, "Oops!"); // declares a static_assert which can render the program ill-formed or have no effect like an empty declaration, depending on the result of expr
template <class T> class C; // declares template class C
; // declares nothing
定义:
extern int a; // Declaration
int a; // Definition
a = 10 // Initialization
int b = 10; // Definition & Initialization
定义将变量与类型关联并分配内存,而声明仅指定类型但不分配内存。在您希望在定义之前引用变量时,声明更加有用。
*不要混淆定义和初始化。两者是不同的,初始化为变量赋值。请参见上面的示例。
以下是一些定义的示例。
int a;
float b;
double c;
int fun(int a,int b);
int b=fun(x,y,z);
#include <stdio.h>
void print(int a)
{
printf("%d",a);
}
main()
{
print(5);
}
#include <stdio.h>
void print(int a); // In this case this is essential
main()
{
print(5);
}
void print(int a)
{
printf("%d",a);
}
这是必要的,因为函数调用在定义之前,编译器必须知道是否存在这样的函数。因此,我们声明将通知编译器的函数。
定义:
定义函数的这个部分叫做定义。它说明了函数内部要做什么。
void print(int a)
{
printf("%d",a);
}
int a; //declaration; a=10; //definition
这完全是错误的。当谈论自动存储期对象时(在函数定义内声明且没有使用其他存储类指示符(如extern)声明的对象),这些都是定义。 - Joey Pabalinas// declaration
int sum(int, int);
sum
的C函数,它接受两个int
类型的参数并返回一个int
类型的值。但是你现在还不能使用它。// definition
int sum(int x, int y)
{
return x + y;
}