sbi*_*sbi 824

声明引入的标识符和描述了它的类型,无论是类型,对象,或功能.声明是编译器接受对该标识符的引用所需的.这些是声明:

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
Run Code Online (Sandbox Code Playgroud)

定义实际实例化/实现此标识符.这是链接器将引用链接到这些实体所需的内容.这些是与上述声明相对应的定义:

int bar;
int g(int lhs, int rhs) {return lhs*rhs;}
double f(int i, double d) {return i+d;}
class foo {};
Run Code Online (Sandbox Code Playgroud)

可以使用定义来代替声明.

可以根据需要随时声明标识符.因此,以下内容在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);
Run Code Online (Sandbox Code Playgroud)

但是,它必须只定义一次.如果您忘记定义已在某处声明和引用的内容,则链接器不知道链接引用的内容和抱怨缺少的符号.如果您多次定义某些内容,则链接器不知道将哪些定义链接引用并抱怨重复的符号.


由于辩论什么是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)在他的一个答案中挖出了实际的章节和经文.

  • @Brian:你错了.`extern int i`是一个声明,因为它只是引入/指定`i`.你可以在每个编译单元中拥有尽可能多的`extern int i`.然而,`int i`是一个定义.它表示整数在此转换单元中的空间,并建议链接器将对此实体的所有引用链接到"i".如果您有多于或少于这些定义中的一个,链接器会抱怨. (11认同)
  • @Brian:"extern int i;" 说我在某处是一个int,不要担心."int i;" 表示我是一个int,它的地址和范围在这里确定. (4认同)
  • @Brian`int i;`在文件/全局范围或函数范围内是C和C++中的定义.在C中,因为它分配存储,在C++中,因为它没有extern说明符或链接规范.这些都是相同的东西,这就是sbi所说的:在这两种情况下,这个声明都指定了必须链接该范围内所有对"i"的引用的对象. (4认同)
  • @unknown,要注意你不能在*class*scope中重新声明成员:`struct A {double f(int,double); double f(int,double); 当然,"无效".但是它允许在其他地方使用.在某些地方你可以声明事物,但也没有定义:`void f(){void g(); }`有效,但不是以下:`void f(){void g(){}};`.在模板方面,什么是定义以及声明有哪些细微的规则 - 小心!虽然+1给了一个好的答案. (4认同)
  • @unknown:要么你的编译器坏了你错误复制了sbi的代码.例如,N1124中的6.7.2(2):"引用相同对象或函数的所有声明都应具有兼容类型;否则,行为未定义." (2认同)
  • “定义可以用来代替声明。” 这可能是不正确的。if 仅在不引入重复定义时才合法。 (2认同)

Mic*_*fik 167

从C++标准3.1节:

一个声明引入名称转换翻译单元或redeclares先前声明引入的名称.声明指定了这些名称的解释和属性.

下一段说明(强调我的)声明是一个定义,除非......

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

void sqrt(double);  // declares sqrt
Run Code Online (Sandbox Code Playgroud)

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

struct X
{
    int a;         // defines a
    static int b;  // declares b
};
Run Code Online (Sandbox Code Playgroud)

...它声明了一个类名

class Y;
Run Code Online (Sandbox Code Playgroud)

...它包含extern没有初始值设定项或函数体的关键字

extern const int i = 0;  // defines i
extern int j;  // declares j
extern "C"
{
    void foo();  // declares foo
}
Run Code Online (Sandbox Code Playgroud)

......或者是一个typedefusing声明.

typedef long LONG_32;  // declares LONG_32
using namespace std;   // declares std
Run Code Online (Sandbox Code Playgroud)

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

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

  • @RJFalconer 你是对的;初始化_不一定_将声明转换为定义(与人们的预期相反;当然我发现这令人惊讶)。除非“b”也被声明为“const”,否则对该示例的修改实际上是非法的。请参阅 http://stackoverflow.com/a/3536513/1858225 和 http://www.daniweb.com/software-development/cpp/threads/140739/take-an-address 。 (2认同)
  • 这对我来说很有趣。根据您的回答,似乎在 C++ 中,声明*也是*定义(有例外),而在 C 标准中,它是从另一个角度来表述的(C99,第 6.7 节,声明):“A *定义*标识符的声明是该标识符的声明:[遵循不同情况的标准]”。我想,以不同的方式看待它。:) (2认同)

pli*_*nth 131

声明:"某处,有一个foo."

定义:"......就在这里!"

  • 声明是为编译器接受一个名称(告诉编译器该名称是合法的,该名称的引入意图不是拼写错误).定义是名称及其内容相关联的位置.链接器使用该定义将名称引用链接到名称的内容. (3认同)

Joh*_*itb 44

C++中有一些有趣的边缘情况(其中一些也在C中).考虑

T t;
Run Code Online (Sandbox Code Playgroud)

这可以是定义或声明,具体取决于类型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".
Run Code Online (Sandbox Code Playgroud)

在C++中,使用模板时,还有另一种边缘情况.

template <typename T>
struct X { 
  static int member; // declaration
};

template<typename T>
int X<T>::member; // definition

template<>
int X<bool>::member; // declaration!
Run Code Online (Sandbox Code Playgroud)

最后一个声明不是定义.它是静态成员的明确特化的声明X<bool>.它告诉编译器:"如果要实例化X<bool>::member,那么不要从主模板中实例化成员的定义,而是使用在别处找到的定义".要使其成为定义,您必须提供初始化程序

template<>
int X<bool>::member = 1; // definition, belongs into a .cpp file.
Run Code Online (Sandbox Code Playgroud)


ada*_*ost 33

宣言

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

定义

定义指定名称描述的代码或数据.必须先声明名称才能使用它.


Ste*_*sop 21

从C99标准来看,6.7(5):

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

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

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

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

然后有一些例子.

有趣的是(或者没有,但我对此感到有些惊讶),typedef int myint;是C99中的一个定义,但只是C++中的一个声明.

  • @sbi:ODR说"(1)任何翻译单元都不得包含任何......类类型的定义"和"(5)类型的定义可以有多个...在一个程序中提供的每个定义出现在不同的翻译单元中"然后一些额外的要求相当于"定义是相同的". (2认同)

Mar*_*Gil 15

来自wiki.answers.com:

术语声明(在C中)表示您正在告诉编译器类型,大小和函数声明的情况,任何变量的参数的类型和大小,或程序中用户定义的类型或函数.没有空间保留在内存中声明的情况下,任何变量.但是,编译器知道在创建此类型的变量的情况下要保留多少空间.

例如,以下是所有声明:

extern int a; 
struct _tagExample { int a; int b; }; 
int myFunc (int a, int b);
Run Code Online (Sandbox Code Playgroud)

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

int a; 
int b = 0; 
int myFunc (int a, int b) { return a + b; } 
struct _tagExample example; 
Run Code Online (Sandbox Code Playgroud)

答案.

  • 这也是错误的(尽管比其他更接近):`struct foo {};`是__definition__,而不是声明.`foo`的声明是`struct foo;`.由此,编译器不知道为`foo`对象保留多少空间. (3认同)

leg*_*s2k 12

C++ 11更新

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

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

  • 不透明的枚举 - enum X : int;
  • 模板参数 - T intemplate<typename T> class MyArray;
  • 参数声明 - xy inint add(int x, int 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" { ... };
  • 类中的静态数据成员 - x inclass C { static int x; };
  • 类/结构声明 - 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
Run Code Online (Sandbox Code Playgroud)


SRI*_*RAN 6

定义 :

extern int a;      // Declaration 
int a;             // Definition
a = 10             // Initialization
int b = 10;        // Definition & Initialization
Run Code Online (Sandbox Code Playgroud)

定义将变量与类型相关联并分配内存,而声明仅指定类型但不分配内存。当您想在定义之前引用变量时,声明更有用。

*不要将定义与初始化混淆。两者都不同,初始化赋予变量值。请参阅上面的示例。

下面是一些定义的例子。

int a;
float b;
double c;
Run Code Online (Sandbox Code Playgroud)

现在函数声明:

int fun(int a,int b); 
Run Code Online (Sandbox Code Playgroud)

注意函数末尾的分号,所以它表示它只是一个声明。编译器知道程序中的某个地方将使用该原型定义该函数。现在,如果编译器得到一个像这样的函数调用

int b=fun(x,y,z);
Run Code Online (Sandbox Code Playgroud)

编译器会报错说没有这个函数。因为它没有该函数的任何原型。

请注意两个程序之间的区别。

方案一

#include <stdio.h>
void print(int a)
{
     printf("%d",a);
}
main()
{
    print(5);
}
Run Code Online (Sandbox Code Playgroud)

在此,print 函数也被声明和定义。由于函数调用是在定义之后进行的。现在看下一个程序。

方案二

 #include <stdio.h>
 void print(int a); // In this case this is essential
 main()
 {
    print(5);
 }
 void print(int a)
 {
     printf("%d",a);
 }
Run Code Online (Sandbox Code Playgroud)

这是必不可少的,因为函数调用在定义之前,所以编译器必须知道是否有任何这样的函数。所以我们声明了将通知编译器的函数。

定义 :

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

void print(int a)
{
    printf("%d",a);
}
Run Code Online (Sandbox Code Playgroud)

  • `int a; //宣言; a=10; //definition` 这是完全错误的。当谈到自动存储持续时间对象(在函数定义中声明的对象,没有用其他存储类说明符(如 extern)声明)时,这些是 **always** 定义。 (3认同)

Kar*_*tor 6

要理解名词,让我们首先关注动词。

宣布- 正式宣布;宣布

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

所以,当你声明一些东西时,你只需告诉它是什么

// declaration
int sum(int, int);
Run Code Online (Sandbox Code Playgroud)

这一行声明了一个被调用的 C 函数sum,它接受两个类型的参数int并返回一个int. 但是,您还不能使用它。

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

// definition
int sum(int x, int y)
{
    return x + y;
}
Run Code Online (Sandbox Code Playgroud)