提问人:Maciek 提问时间:9/11/2009 最后编辑:Jan SchultkeMaciek 更新时间:9/24/2023 访问量:463887
定义和声明有什么区别?
What is the difference between a definition and a declaration?
答:
从 wiki.answers.com:
术语声明的意思是(在 C 中)您告诉编译器有关类型、大小的信息,如果是函数声明,则告诉编译器任何变量的参数的类型和大小,或者程序中用户定义的类型或函数。在声明的情况下,内存中不会为任何变量保留空间。但是,编译器知道在创建此类型的变量时要保留多少空间。
例如,以下是所有声明:
extern int a;
struct _tagExample { int a; int b; };
int myFunc (int a, int b);
另一方面,定义意味着除了声明所做的所有事情之外,内存中还保留了空间。您可以说“DEFINITION = DECLARATION + SPACE RESERVATION”,以下是定义的示例:
int a;
int b = 0;
int myFunc (int a, int b) { return a + b; }
struct _tagExample example;
请参阅答案。
评论
struct foo {};
foo
struct foo;
foo
struct foo;
struct _tagExample { int a; int b; };
:^>
)
声明
声明告诉编译器 程序元素或名称存在。一个 声明介绍一个或多个 名称添加到程序中。声明可以 在一个程序中多次出现。 因此,类、结构、 枚举类型和其他 可以声明用户定义的类型 每个编译单元。
定义
定义指定哪些代码或数据 顾名思义。名称必须是 声明后才能使用。
评论
class foo {};
声明引入标识符并描述其类型,无论是类型、对象还是函数。声明是编译器接受对该标识符的引用所需的内容。这些是声明:
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++ 标准被认为是一个声明和一个定义。(换句话说,“前向声明”用词不当,因为 C++ 中没有其他形式的类声明。struct x;
struct x {};
感谢 litb (Johannes Schaub) 在他的一个答案中挖掘出实际的章节和经文。
评论
extern int i
i
extern int i
int i
i
int i;
struct A { double f(int, double); double f(int, double); };
void f() { void g(); }
void f() { void g() { } };
来自 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
}
...or 是 or 语句。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
根据 C99 标准,6.7(5):
声明指定一组标识符的解释和属性。标识符的定义是该标识符的声明,该标识符:
- 对于对象,导致为该对象保留存储;
- 对于函数,包括函数体;
- 对于枚举常量或 typedef 名称,是 标识符。
根据 C++ 标准 3.1(2):
声明是一个定义,除非它声明一个函数而不指定函数的主体,它包含外部说明符或链接规范,既不包含初始值设定项也不包含函数体,否则它声明类声明中的静态数据成员,它是类名声明,或者它是 typedef 声明、using 声明或 using 指令。
然后有一些例子。
有趣的是(或者不是,但我对此有点惊讶),是 C99 中的定义,但只是 C++ 中的声明。typedef int myint;
评论
typedef
您不能用最笼统的术语来说明,声明是一个标识符,其中没有分配存储,而定义实际上从声明的标识符分配存储吗?
一个有趣的想法 - 在类或函数与类型信息链接之前,模板无法分配存储。那么模板标识符是声明还是定义呢?它应该是一个声明,因为没有分配存储,而你只是在“原型设计”模板类或函数。
评论
template<class T> struct foo;
template<class T> void f();
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;
或者给一个没有body的函数命名、返回类型和参数类型(在函数声明的情况下),例如:
int max(int, int);
而定义意味着为变量赋值(在变量定义的情况下),例如:
i = 20;
或者为函数提供/添加 body(functionality) 称为函数定义,例如:
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()
评论
C++11 更新
由于我没有看到与 C++11 相关的答案,这里有一个。
声明是一个定义,除非它声明了 a/n:
- 不透明的枚举 -
enum X : int;
- template 参数 - T in
template<typename T> class MyArray;
- 参数声明 - X 和 Y
int 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 in
class C { static int x; };
- 类/结构声明 -
struct Point;
- typedef 声明 -
typedef int Int;
- 使用声明 -
using std::cout;
- using 指令 -
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
这听起来很俗气,但这是我能够将术语直截了当的最好方法:
宣言:图片托马斯·杰斐逊(Thomas Jefferson)发表演讲...“我特此声明,此 FOO 存在于此源代码中!!”
定义:想象一本字典,你正在查找 Foo 及其实际含义。
在这里找到类似的答案: C 语言中的技术面试问题。
声明为程序提供名称;定义提供程序中实体(例如类型、实例和函数)的唯一描述。声明可以在给定的作用域中重复,它在给定的作用域中引入一个名称。
声明是一种定义,除非:
- 声明声明一个函数,但不指定其主体,
- 声明包含外部说明符,没有初始值设定项或函数体,
- 声明是没有类定义的静态类数据成员的声明,
- 声明是一个类名定义,
定义是一种声明,除非:
- 定义定义一个静态类数据成员,
- 定义定义非内联成员函数。
当您使用 extern 存储类时,声明和定义的概念将形成一个陷阱,因为您的定义将位于其他位置,并且您在本地代码文件(页面)中声明变量。C 和 C++ 之间的一个区别是,在 C 中,声明通常在函数或代码页的开头完成。在 C++ 中,情况并非如此。您可以在您选择的地点申报。
评论
我最喜欢的例子是“int Num = 5”,这里的变量是 1。定义为 int 2。声明为 Num 和 3。以值 5 进行实例化。我们
- 定义对象的类型,可以是内置的,也可以是类或结构。
- 声明一个对象的名称,因此任何有名称的东西都被声明了,包括变量、函数等。
类或结构允许您更改以后使用对象时定义对象的方式。例如
- 可以声明一个未明确定义的异构变量或数组。
- 在 C++ 中使用偏移量,您可以定义一个没有声明名称的对象。
当我们学习编程时,这两个术语经常被混淆,因为我们经常同时做这两个术语。
评论
定义:
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);
}
在这里,打印函数也被声明和定义。由于函数调用是在定义之后出现的。现在看下一个程序。
计划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
这是完全错误的。在谈论自动存储持续时间对象(在函数定义中声明但未使用其他存储类说明符(如 extern)声明的对象)时,这些对象始终是定义。
声明向编译器提供符号名称。定义是为符号分配空间的声明。
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 # 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)
声明没有等效的指令,因为它不是要执行的东西。
此外,声明告诉编译器变量的作用域。
我们可以说声明是编译器用来建立变量的正确用法以及某些内存属于某个变量多长时间的信息。
要理解名词,让我们先关注动词。
声明 - 正式宣布;宣布
定义 - 清晰完整地展示或描述(某人或某物)
所以,当你声明某件事时,你只需告诉它是什么。
// declaration
int sum(int, int);
此行声明一个 C 函数,该函数采用两个类型的参数并返回一个 .但是,您还不能使用它。sum
int
int
当你提供它的实际工作原理时,这就是它的定义。
// definition
int 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 阶段(翻译器/编译器)中,我们代码中的声明语句告诉编译器我们将来会用到这些东西,您可以稍后找到定义,含义是:
译者确保:What is What ? 表示声明
(3)阶段(链接器)需要定义来绑定事物
链接器确保 : where is what ? 表示定义
K&R(第二版)中散布着一些非常明确的定义;将它们放在一个地方并将它们作为一个整体阅读会有所帮助:
“定义”是指创建变量或分配存储变量的位置;“声明”是指陈述变量性质但没有分配存储的地方。[第33页]
...
区分外部变量的声明及其定义非常重要。声明宣布变量的属性(主要是其类型);定义还会导致存储被留出。 如果线条
int sp; double val[MAXVAL]
出现在任何函数之外,它们定义外部变量和 ,导致存储被搁置,并用作该源文件其余部分的声明。
sp
val
另一方面,线条
extern int sp; extern double val[];
为源文件的其余部分声明,即 和 数组(其大小在其他地方确定),但它们不会创建变量或为它们保留存储空间。
sp
int
val
double
在构成源程序的所有文件中,外部变量必须只有一个定义。...数组大小必须与定义一起指定,但可以通过声明来指定。[第80-81页]
extern
...
声明指定了对每个标识符的解释;它们不一定保留与标识符关联的存储。预留存储的声明称为定义。[第210页]
声明是指在不赋值或对象的情况下创建基元或对象引用变量或方法。 int a; 最终 int a;
定义意味着分别分配值或对象 int a =10;
初始化意味着为相应的变量或对象分配内存。
变量的声明用于向编译器通知以下信息:变量的名称、它所保存的值的类型以及它所采用的初始值(如果有)。即,声明提供了有关变量属性的详细信息。然而,变量的定义说明了变量的存储位置。也就是说,变量的内存是在变量定义期间分配的。
评论
and the initial value
对于声明,则不成立。变量声明永远不能有初始值设定项。
从 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 sampleFunc(); // function
extern int car; // variable
定义说“这个东西存在在这里;为它留下记忆”
int sampleFunc() {} // function
int car; // variable
在对象的定义点,初始化是可选的,并说“这是这个东西的初始值”:
int car = 0; // variable
评论