定义和声明之间有什么区别?

时间:2009-09-11 12:27:13

标签: c declaration terminology definition c++-faq

两者的含义都没有。

25 个答案:

答案 0 :(得分:802)

声明 引入标识符并描述其类型,无论是类型,对象还是函数。声明是 编译器需要 来接受对该标识符的引用。这些是声明:

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)在他的一个答案中挖出了实际章节和经文。

答案 1 :(得分:163)

来自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
}

...或者是typedefusing声明。

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

现在有一个很重要的原因,为什么理解声明和定义之间的区别很重要:一个定义规则。从C ++标准的第3.2.1节开始:

  

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

答案 2 :(得分:126)

声明:“某处,有一个foo。”

定义:“......就在这里!”

答案 3 :(得分:44)

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.

答案 4 :(得分:32)

<强>声明

  

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

<强>定义

  

定义指定代码或数据   这个名字描述。名字必须是   在可以使用之前声明。

答案 5 :(得分:21)

根据C99标准,6.7(5):

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

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

从C ++标准,3.1(2):

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

然后有一些例子。

如此有意思(或者不是,但我对此感到有些惊讶),typedef int myint;是C99中的定义,但只是C ++中的声明。

答案 6 :(得分:15)

来自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

答案 7 :(得分:12)

C ++ 11更新

因为我没有看到与C ++ 11相关的答案。

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

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

上述列表从C ++ 03继承的附加子句:

  • 功能声明 - <{>添加 int add(int x, int y);
  • 包含声明或链接说明符的extern说明符 - extern int a;extern "C" { ... };
  • 类中的静态数据成员 - class C { static int x; };
  • 中的 x
  • class / struct declaration - struct Point;
  • typedef声明 - 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

答案 8 :(得分:4)

声明:

int a; // this declares the variable 'a' which is of type 'int'

因此声明将变量与类型相关联。

以下是一些声明的例子。

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函数。因为函数调用是在定义之后发生的。现在看下一个程序。

计划2

 #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 

有时,声明和定义被分组为一个这样的语句。

int a=10;

答案 9 :(得分:4)

要理解名词,请先关注动词。

声明 - 正式宣布;宣布

定义 - 明确而完整地展示或描述(某人或某事)

因此,当您声明某些内容时,您只需告诉它是什么

// declaration
int sum(int, int);

此行声明一个名为sum的C函数,该函数接受int类型的两个参数并返回int。但是,你还不能使用它。

当你提供它的实际工作方式时,就是它的定义。

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

答案 10 :(得分:4)

经验法则:

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

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

答案 11 :(得分:4)

定义意味着写入的实际功能&amp;声明意味着简单的声明功能 例如

void  myfunction(); //this is simple declaration

void myfunction()
{
 some statement;    
}

这是函数myfunction的定义

答案 12 :(得分:3)

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

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)

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

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

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

答案 13 :(得分:2)

在此处找到类似的答案:Technical Interview Questions in C

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

声明是一个定义,除非

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

定义是声明,除非:

  • 定义定义静态类数据成员
  • 定义定义非内联成员函数。

答案 14 :(得分:2)

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

一个有趣的想法 - 在类或函数与类型信息链接之前,模板无法分配存储。那么模板标识符是声明还是定义?它应该是一个声明,因为没有分配存储空间,而你只是简单地“模板化”模板类或函数。

答案 15 :(得分:1)

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

  

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

答案 16 :(得分:1)

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

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

定义:描绘一本字典,你正在查找Foo及其实际含义。

答案 17 :(得分:1)

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

int f(int x); // function declaration (I know f exists)

int f(int x) { return 2*x; } // declaration and definition

答案 18 :(得分:1)

声明意味着给变量赋予名称和类型(如果是变量声明) 例如:

 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和函数max()

答案 19 :(得分:0)

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

  • 定义对象的类型,可以是内置类或类或结构。
  • 声明对象的名称,因此声明了包含变量,功能等的任何名称。

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

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

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

答案 20 :(得分:0)

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

答案 21 :(得分:0)

可执行文件生成阶段:

  

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

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

  

翻译者确保:什么是什么?意味着声明

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

  

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

答案 22 :(得分:0)

K&R(第二版)中散布着一些非常清晰的定义;有助于将它们放在一个地方并作为一个地方阅读:

  

“定义”是指创建或分配变量的位置; “声明”是指声明变量性质但未分配存储空间的地方。 [p。 33]

     

...

     

重要的是要区分外部变量的声明和其定义。声明声明变量的属性(主要是其类型);定义也会导致存储被搁置。    如果行

int sp;
double val[MAXVAL]
     

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

     

另一方面,线条

extern int sp;
extern double val[];
     

声明源文件的其余部分,spintvaldouble数组(其大小为确定),但它们不会创建变量或为其保留存储空间。

     

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

     

...

     

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

答案 23 :(得分:0)

声明是在创建原始或对象引用变量或方法而未分配值或对象时进行的。 诠释 最终诠释a;

定义意味着分别分配值或对象 int a = 10;

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

答案 24 :(得分:0)

变量的

声明用于通知编译器以下信息:变量的名称、它保存的值的类型和初始值(如果有的话)。即,声明提供有关变量属性的详细信息。而变量的定义说明了变量的存储位置。即在变量定义期间为变量分配内存。