定义和声明有什么区别?

两者的意义都让我困惑。

435117 次浏览

wiki.answers.com:

术语声明意味着(在C中)你告诉编译器关于类型、大小和在函数声明的情况下,任何变量的参数的类型和大小,或程序中用户定义的类型或函数。空间在内存中为声明的情况下的任何变量保留。然而编译器知道在创建这种类型的变量时保留多少空间。

例如,以下是所有声明:

extern int a;struct _tagExample { int a; int b; };int myFunc (int a, int b);

另一方面,定义意味着除了声明所做的所有事情之外,空间也被保留在内存中。你可以说“DEFINITION=DECLARATE+SPACE RESERVION”以下是定义的例子:

int a;int b = 0;int myFunc (int a, int b) { return a + b; }struct _tagExample example;

答案

宣言

声明告诉编译器存在程序元素或名称。A声明引入一个或多个名称到程序中。声明可以在一个程序中出现不止一次。因此,类,结构,枚举类型和其他用户定义的类型可以声明为每个编译单元。

定义

定义指定哪些代码或数据名字描述。一个名字必须是

在使用之前声明。

声明引入了一个标识符并描述了它的类型,无论是类型、对象还是函数。声明编译器需要什么接受对该标识符的引用。这些是声明:

extern int bar;extern int g(int, int);double f(int, double); // extern can be omitted for function declarationsclass foo; // no extern allowed for type declarations

A定义实际上实例化/实现了这个标识符。它的链接器需要什么是为了链接到这些实体的引用。这些是与上述声明对应的定义:

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 aboveextern 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++中没有其他形式的类声明)。

感谢约翰·肖布(Johannes Schaub)在他的一个答案中挖掘出了实际的章节和诗句。

从C++标准3.1节:

A声明将名称引入翻译单元或重新声明由前一个单元引入的名称声明。声明指定了这些名称的解释和属性。

下一段指出(强调我的)声明是一个定义除非…

…它声明了一个函数而没有指定函数的主体:

void sqrt(double);  // declares sqrt

…它在类定义中声明了一个静态成员:

struct X{int a;         // defines astatic int b;  // declares b};

…它声明了一个类名:

class Y;

…它包含extern关键字,没有初始化器或函数体:

extern const int i = 0;  // defines iextern int j;  // declares jextern "C"{void foo();  // declares foo}

…或者是typedefusing语句。

typedef long LONG_32;  // declares LONG_32using namespace std;   // declares std

现在,理解声明和定义之间的区别很重要的原因是:一个定义规则。来自C++标准的第3.2.1节:

任何翻译单元不得包含任何变量、函数、类类型、枚举类型或模板的多个定义。

根据C99标准,6.7(5):

声明指定一组标识符的解释和属性。标识符的定义是该标识符的声明,即:

  • 对于对象,导致为该对象保留存储空间;
  • 对于函数,包括函数体;
  • 对于枚举常量或typedef名称,是枚举常量或typedef名称的(唯一)声明标识符。

根据C++标准,3.1(2):

声明是定义,除非它声明了一个函数而没有指定函数的主体,它包含extern说明符或链接规范,既不是初始化器也不是函数主体,它在类声明中声明了一个静态数据成员,它是一个类名声明,或者它是一个typedef声明,一个using-声明或一个using-指令。

然后有一些例子。

所以有趣的是(或者不是,但我有点惊讶),typedef int myint;是C99中的定义,但只是C++中的声明。

您不能用最一般的术语说明,声明是一个标识符,其中没有分配存储,定义实际上从声明的标识符分配存储吗?

一个有趣的想法是——在类或函数与类型信息链接之前,模板不能分配存储。那么模板标识符是声明还是定义呢?它应该是一个声明,因为没有分配存储,您只是在“原型化”模板类或函数。

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.

声明:“某处,存在一个foo。

定义:“……在这里!”

定义表示编写的实际函数,声明表示简单声明函数例如

void  myfunction(); //this is simple declaration

void myfunction(){some statement;}

这是函数myFunction的定义

经验法则:

  • 声明告诉编译器如何解释内存中变量的数据。每次访问都需要这一点。

  • 定义保留内存以使变量存在。这必须在第一次访问之前恰好发生一次。

声明意味着给变量命名和类型(在变量声明的情况下),例如:

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;}

在上述情况下,我们定义并声明变量ifunction max()

C++11更新

由于我没有看到与C++11相关的答案,这里有一个。

声明是定义,除非它声明了a/n:

  • 不透明枚举-enum X : int;
  • 模板参数-T intemplate<typename T> class MyArray;
  • 参数声明-int add(int x, int y);中的xy
  • 别名声明-using IntVector = std::vector<int>;
  • 静态断言声明-static_assert(sizeof(int) == 4, "Yikes!")
  • 属性声明(实现定义)
  • 空声明;

上述列表从C++03继承的附加条款:

  • 函数声明-添加 inint add(int x, int y);
  • 包含声明或链接说明符的extern说明符-extern int a;extern "C" { ... };
  • 类中的静态数据成员-x inclass C { static int x; };
  • 类/结构声明-struct Point;
  • typedef声明-typedef int Int;
  • 使用声明-using std::cout;
  • 使用指令-using namespace NS;

模板声明是一个声明。如果模板声明定义了函数、类或静态数据成员,则模板声明也是一个定义。

标准中区分声明和定义的示例,我发现这些示例有助于理解它们之间的细微差别:

// except one all these are definitionsint a;                                  // defines aextern const int c = 1;                 // defines cint f(int x) { return x + a; }          // defines f and defines xstruct S { int a; int b; };             // defines S, S::a, and S::bstruct X {                              // defines Xint x;                              // defines non-static data member xstatic int y;                       // DECLARES static data member yX(): x(0) { }                       // defines a constructor of X};int X::y = 1;                           // defines X::yenum { up , down };                     // defines up and downnamespace N { int d; }                  // defines N and N::dnamespace N1 = N;                       // defines N1X anX;                                  // defines anX

// all these are declarationsextern int a;                           // declares aextern const int c;                     // declares cint f(int);                             // declares fstruct S;                               // declares Stypedef int Int;                        // declares Intextern X anotherX;                      // declares anotherXusing N::d;                             // declares N::d

// specific to C++11 - these are not from the standardenum X : int;                           // declares X with int as the underlying typeusing 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 exprtemplate <class T> class C;             // declares template class C;                                       // declares nothing

这听起来真的很俗气,但这是我能够在脑海中保持这些条款的最佳方式:

声明:图片托马斯·杰斐逊发表演讲……“我在此宣布,这个FOO存在于这个源代码中!!!”

定义:想象一本字典,你正在查找Foo以及它的实际含义。

在这里找到类似的答案:C中的技术面试问题

声明为程序提供了一个名称;定义提供了程序中实体(例如类型、实例和函数)的唯一描述。声明可以在给定范围内重复,它在给定范围内引入了一个名称。

声明是一个定义,除非:

  • 声明声明了一个函数而没有指定它的主体,
  • 声明包含一个extern说明符,没有初始化器或函数体,
  • 声明是没有类定义的静态类数据成员的声明,
  • 声明是一个类名定义,

定义是声明,除非:

  • 定义定义了一个静态类数据成员,
  • 定义定义一个非内联成员函数。

当你使用extern储存类别时,声明和定义的概念将成为一个陷阱,因为你的定义将在其他位置,并且你在本地代码文件(页面)中声明变量。C和C++的一个区别是,在C中,你的声明通常是在函数或代码页的开头完成的。C++不是这样的。你可以在你选择的地方声明。

我最喜欢的例子是“int Num=5”,这里您的变量是1.定义为int 2.声明为Num和3.实例化值为5。我们

  • 定义对象的类型,可以是内置的,也可以是类或结构。
  • 声明对象的名称,因此任何具有名称的东西都已声明,其中包括变量、函数等。

类或结构允许您更改以后使用对象时定义对象的方式。例如

  • 可以声明一个未具体定义的异构变量或数组。
  • 在C++中使用偏移量可以定义一个没有声明名称的对象。

当我们学习编程时,这两个术语经常被混淆,因为我们经常同时做这两件事。

定义:

extern int a;      // Declarationint a;             // Definitiona = 10             // Initializationint 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);}

在这里,打印函数也被声明和定义。因为函数调用在定义之后。现在看下一个程序。

方案2

 #include <stdio.h>void print(int a); // In this case this is essentialmain(){print(5);}void print(int a){printf("%d",a);}

这很重要,因为函数调用先于定义,所以编译器必须知道是否有这样的函数。所以我们声明了会通知编译器的函数。

定义:

定义函数的这部分称为定义。它说明了在函数内部要做什么。

void print(int a){printf("%d",a);}

声明向编译器提供符号名称。定义是为符号分配空间的声明。

int f(int x); // function declaration (I know f exists)
int f(int x) { return 2*x; } // declaration and definition

要了解声明和定义之间的区别,我们需要查看汇编代码:

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        # 0x400a20movsd   %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        # 0x400a20movsd   %xmm0,-0x8(%rbp)

你可以看到没有什么变化。

声明与定义不同,因为它提供的信息仅供编译器使用。例如uint8_t告诉编译器使用asm函数movb。

注意:

uint def;                  |  no instructionsprintf("some stuff...");   |  [...] callq   0x400450 <printf@plt>def=5;                     |  movb    $0x5,-0x45(%rbp)

声明没有等价的指令,因为它不是要执行的东西。

此外,声明告诉编译器变量的范围。

我们可以说声明是编译器用来确定变量的正确使用以及某些内存属于某个变量的时间的信息。

为了理解名词,让我们先关注动词。

宣布-正式宣布;宣告

定义-清楚而完整地显示或描述(某人或某物)

所以,当你声明某事时,你只需告诉它是什么

// declarationint sum(int, int);

这行宣布是一个名为sum的C函数,它接受两个int类型的参数并返回一个int。但是,您还不能使用它。

当你提供它是如何工作的时,这就是它的定义。

// definitionint sum(int x, int y){return x + y;}

根据GNU C库手册(http://www.gnu.org/software/libc/manual/html_node/Header-Files.html

在C语言中,声明仅仅提供了一个函数或变量存在的信息并给出了它的类型。对于函数声明,也可以提供关于其参数类型的信息。声明的目的是允许编译器正确处理对声明的变量和函数的引用。另一方面,定义实际上为变量分配存储空间或说明函数的作用。

可执行文件生成的阶段:

(1)预处理器->(2)翻译/编译器->(3)链接器

在第2阶段(翻译/编译器),我们代码中的声明语句告诉编译器我们将来会使用这些东西,您可以稍后找到定义,意思是:

翻译确保:什么是什么?表示声明

(3)阶段(链接器)需要定义来绑定事物

链接器确保:什么在哪里?表示定义

在K&R(第2版)中有一些非常明确的定义;将它们放在一个地方并将它们作为一个整体阅读是有帮助的:

“定义”是指变量被创建或分配存储的地方;“声明”是指变量的性质被声明但没有分配存储的地方。[p.33]

区分外部变量的声明和它的定义很重要。声明宣布变量的属性(主要是它的类型);定义还会导致存储被预留。如果行

int sp;double val[MAXVAL]

出现在任何函数之外,它们定义外部变量spval,导致存储被留出,并作为该源文件其余部分的声明。

另一方面,线条

extern int sp;extern double val[];

宣布对于源文件的其余部分,spintvaldouble数组(其大小在其他地方确定),但它们不会为它们创建变量或保留存储空间。

在构成源程序的所有文件中,必须只有一个外部变量的定义…数组大小必须用定义指定,但用extern声明是可选的。[pp.80-81]

声明指定对每个标识符的解释;它们不一定保留与标识符关联的存储。保留存储的声明称为定义。[p.210]

声明是在没有分配值或对象的情况下创建原始或对象引用变量或方法。int a;最后一个int a;

定义是指分别为值或对象赋值int a=10;

初始化意味着为相应的变量或对象分配内存。

变量的宣言用于通知编译器以下信息:变量的名称、它持有的值的类型和初始值(如果有的话)。即,声明给出了有关变量属性的详细信息。然而,变量的定义说明了变量的存储位置。也就是说,变量的内存是在变量的定义过程中分配的。

添加来自C++标准文档的定义和声明示例(来自第3.1节声明和定义)

定义:

int a;                       // defines aextern const int c = 1;      // defines cint f(int x) { return x+a; } // defines f and defines xstruct S { int a; int b; };  // defines S, S::a, and S::bstruct X {                   // defines Xint x;                   // defines non-static data member xstatic int y;            // DECLARES static data member yX(): x(0) { }            // defines a constructor of X};int X::y = 1;                // defines X::yenum { up, down };           // defines up and downnamespace N { int d; }       // defines N and N::dnamespace N1 = N;            // defines N1X anX;                       // defines anX

声明:

extern int a;                 // declares aextern const int c;           // declares cint f(int);                   // declares fstruct S;                     // declares Stypedef int Int;              // declares Intextern X anotherX;            // declares anotherXusing N::d;                   // declares d

宣言表示“这个东西存在于某处”

int sampleFunc(); // functionextern int car;  // variable

定义表示“这个东西存在于这里;为它创建内存”

int sampleFunc() {} // functionint car; // variable

初始化过程在对象的定义点是可选的,并表示“这是这个东西的初始值”:

int car = 0; // variable