两者的含义都让我无法理解。
声明引入了一个标识符并描述了它的类型,无论是类型、对象还是函数。声明是编译器需要接受对该标识符的引用。这些是声明:
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++ 标准的引用。在 3.1/2,C++03 说:
一个声明就是一个定义,除非它 [...] 是一个类名声明 [...]。
3.1/3 再举几个例子。其中:
[Example: [...] struct S { int a; int b; }; // defines S, S::a, and S::b [...] struct S; // declares S —end example
总结一下:C++ 标准认为 struct x;
是一个声明,而 struct x {};
是一个定义。 (换句话说,“前向声明”用词不当,因为在 C++ 中没有其他形式的类声明。)
感谢litb (Johannes Schaub)在他的一个答案中挖掘出实际的章节和诗句。
从 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++ 标准的第 3.2.1 节:
任何翻译单元都不得包含一个以上的任何变量、函数、类类型、枚举类型或模板的定义。
struct x {static int b = 3; };
吗?
b
也被声明为 const
。请参阅 stackoverflow.com/a/3536513/1858225 和 daniweb.com/software-development/cpp/threads/140739/… 。
声明:“某处,存在一个 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 {};
是一个类定义,不是吗?
根据 C99 标准,6.7(5):
声明指定一组标识符的解释和属性。标识符的定义是该标识符的声明:
对于一个对象,导致为该对象保留存储空间;
对于函数,包括函数体;
对于枚举常量或 typedef 名称,是标识符的(唯一)声明。
根据 C++ 标准 3.1(2):
声明是一个定义,除非它声明一个函数而不指定函数的主体,它包含外部说明符或链接规范,既不是初始化程序也不是函数体,它在类声明中声明一个静态数据成员,它是类名声明,或者它是一个 typedef 声明、一个 using 声明或一个 using 指令。
然后有一些例子。
如此有趣(或不是,但我对此有点惊讶),typedef int myint;
是 C99 中的定义,但只是 C++ 中的声明。
typedef
,这是否意味着它可以在 C++ 中重复,但不能在 C99 中重复?
来自 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
对象保留多少空间。
struct foo;
是一个声明,但它不会告诉编译器 foo 的大小。我要补充的是 struct _tagExample { int a; int b; };
是一个定义。因此,在这种情况下,将其称为声明是一种误导。当然它是一个,因为所有定义都是声明,但您似乎暗示它不是定义。它是 _tagExample 的定义。
C++11 更新
因为我没有看到与 C++11 相关的答案,所以这里有一个。
除非声明 a/n,否则声明就是定义:
不透明枚举 - 枚举 X : int;
模板参数 - T in template
参数声明 - x 和 y int add(int x, int y);
别名声明 - using IntVector = std::vector
静态断言声明 - static_assert(sizeof(int) == 4, "Yikes!")
属性声明(实现定义)
空声明;
上述列表从 C++03 继承的附加子句:
函数声明 - 添加 int add(int x, int y);
包含声明或链接说明符的外部说明符 - extern int a;或 extern "C" { ... };
类中的静态数据成员 - 类 C 中的 x { static int x; };
类/结构声明 - 结构点;
typedef 声明 - typedef int Int;
使用声明 - 使用 std::cout;
using 指令 - 使用命名空间 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);
编译器会抛出一个错误,说没有这样的函数。因为它没有该功能的任何原型。
注意两个程序之间的区别。
程序 1
#include <stdio.h>
void print(int a)
{
printf("%d",a);
}
main()
{
print(5);
}
在此,还声明和定义了 print 函数。由于函数调用是在定义之后进行的。现在看下一个程序。
节目二
#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 声明的对象)时,这些是 always 定义。
要理解名词,让我们首先关注动词。
宣布 - 正式宣布;宣布
定义 - 清楚完整地显示或描述(某人或某事)
因此,当您声明某事时,您只需说出它是什么。
// declaration
int sum(int, int);
这行声明了一个名为 sum
的 C 函数,它接受两个 int
类型的参数并返回一个 int
。但是,您还不能使用它。
当您提供它的实际工作方式时,这就是它的定义。
// definition
int sum(int x, int y)
{
return x + y;
}
定义表示实际编写的函数,声明表示简单的声明函数,例如
void myfunction(); //this is simple declaration
和
void myfunction()
{
some statement;
}
这是函数 myfunction 的定义
经验法则:
声明告诉编译器如何解释变量在内存中的数据。这是每次访问都需要的。
定义保留内存以使变量存在。这必须在首次访问之前恰好发生一次。
要了解声明和定义之间的区别,我们需要查看汇编代码:
uint8_t ui8 = 5; | movb $0x5,-0x45(%rbp)
int i = 5; | movl $0x5,-0x3c(%rbp)
uint32_t ui32 = 5; | movl $0x5,-0x38(%rbp)
uint64_t ui64 = 5; | movq $0x5,-0x10(%rbp)
double doub = 5; | movsd 0x328(%rip),%xmm0 # 0x400a20
movsd %xmm0,-0x8(%rbp)
这只是定义:
ui8 = 5; | movb $0x5,-0x45(%rbp)
i = 5; | movl $0x5,-0x3c(%rbp)
ui32 = 5; | movl $0x5,-0x38(%rbp)
ui64 = 5; | movq $0x5,-0x10(%rbp)
doub = 5; | movsd 0x328(%rip),%xmm0 # 0x400a20
movsd %xmm0,-0x8(%rbp)
如您所见,没有任何变化。
声明与定义不同,因为它提供了仅供编译器使用的信息。例如 uint8_t 告诉编译器使用 asm 函数 movb。
看到:
uint def; | no instructions
printf("some stuff..."); | [...] callq 0x400450 <printf@plt>
def=5; | movb $0x5,-0x45(%rbp)
声明没有等效的指令,因为它没有要执行的东西。
此外,声明告诉编译器变量的范围。
我们可以说声明是编译器用来确定变量的正确使用以及某些内存属于某个变量的时间长度的信息。
在这里找到类似的答案:Technical Interview Questions in C。
声明为程序提供了一个名称;定义提供程序中实体(例如类型、实例和功能)的唯一描述。声明可以在给定范围内重复,它在给定范围内引入名称。
声明是一个定义,除非:
声明声明一个函数而不指定它的主体,
声明包含一个外部说明符,没有初始化程序或函数体,
声明是没有类定义的静态类数据成员的声明,
声明是一个类名定义,
定义是声明,除非:
定义定义了一个静态类数据成员,
定义定义了一个非内联成员函数。
您不能用最一般的术语来说明,声明是一个没有分配存储空间的标识符,而定义实际上是从声明的标识符分配存储空间的吗?
一个有趣的想法 - 在类或函数与类型信息链接之前,模板无法分配存储空间。那么模板标识符是声明还是定义?它应该是一个声明,因为没有分配存储空间,您只是在“原型化”模板类或函数。
template<class T> struct foo;
是一个模板声明,这个 template<class T> void f();
也是如此。模板定义以相同的方式镜像类/函数定义。 (请注意,template name 不是 type 或 function name。当您无法将模板传递为另一个模板的类型参数。如果要传递模板而不是类型,则需要模板模板参数。)
这听起来很俗气,但这是我能够在脑海中保持这些术语的最佳方式:
声明:图片 Thomas Jefferson 发表演讲……“我在此声明此 FOO 存在于此源代码中!!!”
定义:想象一本字典,你正在查找 Foo 以及它的实际含义。
声明向编译器提供符号名称。定义是为符号分配空间的声明。
int f(int x); // function declaration (I know f exists)
int f(int x) { return 2*x; } // declaration and definition
根据 GNU C 库手册 (http://www.gnu.org/software/libc/manual/html_node/Header-Files.html)
在 C 中,声明仅提供函数或变量存在的信息并给出其类型。对于函数声明,也可能会提供有关其参数类型的信息。声明的目的是允许编译器正确处理对声明的变量和函数的引用。另一方面,定义实际上为变量分配存储空间或说明函数的作用。
变量的声明是为了通知编译器以下信息:变量的名称、它所持有的值的类型和初始值(如果有的话)。即,声明提供了有关变量属性的详细信息。然而,变量的定义说明了变量的存储位置。即,变量的内存是在变量定义期间分配的。
当您使用外部存储类时,声明和定义的概念将形成一个陷阱,因为您的定义将位于其他位置并且您在本地代码文件(页面)中声明变量。 C 和 C++ 之间的一个区别是,在 C 中,声明通常在函数或代码页的开头完成。在 C++ 中,情况并非如此。您可以在您选择的地方申报。
我最喜欢的例子是“int Num = 5”,这里你的变量是 1. 定义为 int 2. 声明为 Num 和 3. 实例化值为 5。我们
定义对象的类型,可以是内置的,也可以是类或结构。
声明一个对象的名字,所以任何有名字的东西都被声明了,包括变量、函数等。
类或结构允许您在以后使用对象时更改对象的定义方式。例如
可以声明一个未明确定义的异构变量或数组。
在 C++ 中使用偏移量,您可以定义一个没有声明名称的对象。
当我们学习编程时,这两个术语经常被混淆,因为我们经常同时做这两个。
可执行文件生成的阶段:
(1) 预处理器 -> (2) 翻译器/编译器 -> (3) 链接器
在第 2 阶段(翻译器/编译器)中,我们代码中的声明语句告诉编译器我们将在未来使用这些东西,您可以稍后找到定义,含义是:
译者确定:什么是什么?意味着声明
和(3)阶段(链接器)需要定义来绑定东西
链接器确定:什么在哪里?手段定义
K&R(第 2 版)中散布着一些非常明确的定义;将它们放在一个地方并将它们作为一个整体阅读会有所帮助:
“定义”是指变量被创建或分配存储的地方; “声明”是指声明变量性质但没有分配存储空间的地方。 [页。 33] ...区分外部变量的声明和它的定义是很重要的。声明宣布变量的属性(主要是它的类型);定义也会导致存储空间被搁置。如果行 int sp; double val[MAXVAL] 出现在任何函数之外,它们定义外部变量 sp 和 val,导致存储空间被搁置,并且还用作该源文件其余部分的声明。另一方面,行 extern int sp;外部双 val[];为源文件的其余部分声明 sp 是一个 int 并且 val 是一个双精度数组(其大小在别处确定),但它们不会为它们创建变量或保留存储空间。在构成源程序的所有文件中,必须只有一个外部变量定义。 ...数组大小必须在定义中指定,但对于 extern 声明是可选的。 [页。 80-81] ...声明指定了对每个标识符的解释;它们不一定保留与标识符关联的存储空间。保留存储的声明称为定义。 [页。 210]
声明是在未分配值或对象的情况下创建原始或对象引用变量或方法时。诠释一个;最终诠释一个;
定义的意思是分别给值或对象赋值 int a =10;
初始化意味着为相应的变量或对象分配内存。
从 C++ 标准文档添加定义和声明示例(来自第 3.1 节声明和定义)
定义:
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
声明:
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
声明意味着为变量指定名称和类型(在变量声明的情况下),例如:
int i;
或为没有主体的函数提供名称、返回类型和参数类型(在函数声明的情况下),例如:
int max(int, int);
而定义意味着为变量赋值(在变量定义的情况下),例如:
i = 20;
或向函数提供/添加主体(功能)称为函数定义,例如:
int max(int a, int b)
{
if(a>b) return a;
return b;
}
多次声明和定义可以一起完成:
int i=20;
和:
int max(int a, int b)
{
if(a>b) return a;
return b;
}
在上述情况下,我们定义并声明变量 i
和 function max()
。
extern int i
是一个声明,因为它只是引入/指定了i
。您可以在每个编译单元中拥有任意数量的extern int i
。然而,int i
是一个定义。它表示整数在此翻译单元中的空间,并建议链接器将所有对i
的引用链接到此实体。如果您的定义多于或少于其中一个,链接器会抱怨。int i;
在文件/全局范围或函数范围内是 C 和 C++ 中的定义。在 C 中是因为它分配存储空间,而在 C++ 中是因为它没有 extern 说明符或链接规范。这些相当于同一件事,这就是 sbi 所说的:在这两种情况下,此声明都指定了该范围内对“i”的所有引用都必须链接到的对象。struct A { double f(int, double); double f(int, double); };
当然无效。不过在其他地方是允许的。在某些地方您可以声明事物,但也不能定义:void f() { void g(); }
有效,但以下不是:void f() { void g() { } };
。当涉及到模板时,什么是定义以及什么声明具有微妙的规则 - 请注意! +1虽然是一个很好的答案。