两者的含义使我难以理解。
两者的含义使我难以理解。
Answers:
甲声明引入的标识符和描述了它的类型,无论是类型,对象,或功能。声明是编译器需要接受对该标识符的引用的内容。这些是声明:
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然后给出一些例子。其中:
[示例:[...] 结构S {int a; int b; }; //定义S,S :: a和S :: b [...] 结构S; //声明S -结束示例
概括起来:C ++标准认为struct x;
是一个声明和struct x {};
一个定义。(换句话说,“前向声明”是不正确的用词,因为C ++中没有其他形式的类声明。)
感谢litb(Johannes Schaub)在他的答案之一中挖掘了实际的章节。
extern int i
是声明,因为它只是引入/指定i
。extern int i
每个编译单元中可以有任意多个。int i
但是,这是一个定义。它表示该转换单元中整数的空间,并建议链接器i
将此实体的所有引用链接到该实体。如果您定义的定义多于或少于这些定义之一,则链接器将发出投诉。
int i;
文件/全局作用域或函数作用域中的@Brian 都是C和C ++中的定义。在C中,因为它分配存储,在C ++中,因为它没有外部说明符或链接规范。这些相当于同一件事,即sbi所说的:在两种情况下,此声明都指定该范围内所有对“ i”的引用都必须链接到的对象。
struct A { double f(int, double); double f(int, double); };
当然是无效的。虽然允许在其他地方。在某些地方您可以声明事物,但也不能定义它们:void f() { void g(); }
有效,但不能定义以下内容:void f() { void g() { } };
。关于模板,定义是什么,声明有微妙的规则-请当心!+1虽然是个不错的答案。
从C ++标准第3.1节开始:
一个声明介绍名称到由先前的声明引入了一个翻译单元或redeclares名称。声明指定这些名称的解释和属性。
下一段指出(强调我的意思)声明是一个定义,除非...
...它在不指定函数主体的情况下声明了一个函数:
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
or 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):
声明指定一组标识符的解释和属性。标识符的定义是对该标识符的声明:
根据C ++标准3.1(2):
声明是一个定义,除非它在不指定函数主体的情况下声明函数,它包含extern说明符或链接规范,并且既不包含初始化程序也不包含函数体,它在类声明中声明一个静态数据成员,它是一个类名声明,或者是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);
另一方面,定义意味着除了声明所做的所有事情之外,空间还保留在内存中。您可以说“ 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;
是一个声明,但它不会告诉编译器foo的大小。我要补充一下,这struct _tagExample { int a; int b; };
是一个定义。因此,在这种情况下,将其称为声明是令人误解的。当然,它是一个,因为所有定义都是声明,但是您似乎暗示它不是定义。它是_tagExample的定义。
由于我看不到与C ++ 11相关的答案,所以这里是一个。
声明是定义,除非声明a / n:
enum X : int;
template<typename T> class MyArray;
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 int a;
或extern "C" { ... };
class C { static int x; };
struct Point;
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
定义:
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)声明的对象)时,它们始终是定义。
经验法则:
一个声明告诉编译器如何解释在内存中变量的数据。每次访问都需要这样做。
一个定义保留内存,使现有的变量。必须在第一次访问之前进行一次。
要了解声明和定义之间的区别,我们需要查看汇编代码:
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)
声明没有等效的指令,因为它没有要执行的内容。
此外,声明告诉编译器变量的范围。
可以说,声明是编译器用来建立变量的正确用法以及某些内存属于某个变量的时间的信息。
您能否用最笼统的术语陈述一下,即声明是没有分配存储空间的标识符,而定义实际上是从声明的标识符分配存储空间的?
一个有趣的想法-在类或函数与类型信息链接之前,模板无法分配存储。那么模板标识符是声明还是定义?由于未分配存储空间,因此应该是一个声明,而您只是在“模板化”模板类或函数。
template<class T> struct foo;
是模板声明,这也是template<class T> void f();
。模板定义以相同的方式镜像类/函数定义。(请注意,模板名称不是类型或函数名称。可以在此处看到的地方是无法将模板作为另一个模板的type参数传递。如果要传递模板而不是类型,则需要模板模板参数。 )
根据GNU C库手册(http://www.gnu.org/software/libc/manual/html_node/Header-Files.html)
在C语言中,声明仅提供有关函数或变量存在并给出其类型的信息。对于函数声明,可能还会提供有关其参数类型的信息。声明的目的是允许编译器正确处理对声明的变量和函数的引用。另一方面,定义实际上是为变量分配存储空间或说明函数的功能。
我最喜欢的示例是“ int Num = 5”,这里您的变量是1.定义为int 2.声明为Num,并且3.实例化为5。我们
类或结构允许您更改以后使用对象时定义对象的方式。例如
当我们学习编程时,这两个术语经常会混淆,因为我们经常同时做这两个事情。
可执行文件生成的阶段:
(1)预处理器->(2)转换器/编译器->(3)链接器
在第2阶段(翻译器/编译器)中,代码中的声明语句告诉编译器我们将来将要使用的这些东西,您以后可以找到定义,其含义是:
译者确保:什么是什么?手段申报
(3)阶段(链接器)需要定义绑定事物
链接器确保:哪里是什么?手段定义
K&R(第二版)中散布着一些非常清晰的定义。它有助于将它们放在一个地方并作为一个地方阅读:
“定义”是指创建或分配变量的位置;“声明”是指声明变量性质但未分配存储空间的地方。[p。33]
...
区分外部变量的声明及其定义很重要。声明声明变量的属性(主要是其类型);定义也会导致存储被搁置。如果线路
int sp; double val[MAXVAL]
出现在任何函数之外,它们定义外部变量
sp
和val
,导致存储被搁置,并且还充当该源文件其余部分的声明。另一方面,线条
extern int sp; extern double val[];
为源文件的其余部分声明一个数组
sp
,int
并声明val
该double
数组(其大小在其他位置确定),但是它们不会创建变量或为其保留存储空间。在组成源程序的所有文件中,外部变量的定义必须只有一个。...数组大小必须通过定义指定,但在
extern
声明时是可选的。[pp。80-81]...
声明指定对每个标识符的解释;它们不一定保留与标识符关联的存储。保留存储的声明称为定义。[p。210]
声明是指为变量指定名称和类型(在变量声明的情况下),例如:
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()
。
int x;