定义和声明有什么区别?
两者的意义都不见了。
一个声明引入了一个标识符,并描述了它的types,无论是types,对象还是function。 声明是编译器需要接受对该标识符的引用。 这些是声明:
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然后举几个例子。 其中:
[例如: struct S {int a; int b; }; //定义S,S :: a和S :: b [...] 结构S; //声明S - 例如
总结一下:C ++标准认为struct x;
成为一个声明和struct x {};
一个定义 。 (换句话说, “前向声明”是一个误称 ,因为在C ++中没有其他forms的类声明。)
感谢litb(Johannes Schaub)在他的一个答案中挖掘了实际的章节和诗句。
从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 }
…或者是一个typedef
或using
语句。
typedef long LONG_32; // declares LONG_32 using namespace std; // declares std
现在理解声明和定义之间的区别是很重要的重要原因: 一个定义规则 。 从C ++标准的第3.2.1节:
任何翻译单元都不得包含任何variables,函数,类types,枚举types或模板的多个定义。
宣言“某处,存在一个富”。
定义:“…在这里!”
在C ++中有一些有趣的边缘情况(其中一些也在C中)。 考虑
T t;
这可以是定义或声明,具体取决于T
是什么types:
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
,那么不要从主模板实例化成员的定义,而是使用在其他地方find的定义”。 为了使它成为一个定义,你必须提供一个初始化器
template<> int X<bool>::member = 1; // definition, belongs into a .cpp file.
宣言
声明告诉编译器存在一个程序元素或名称。 声明将一个或多个名称引入程序中。 声明可以在程序中多次出现。 因此,可以为每个编译单元声明类,结构,枚举types和其他用户定义的types。
定义
定义指定名称描述的代码或数据。 名称必须先声明,然后才能使用。
从C99标准6.7(5):
声明指定了一组标识符的解释和属性。 标识符的定义是对该标识符的声明:
- 对于一个对象,导致为该对象保留存储;
- 对于一个function,包括function体;
- 对于枚举常量或typedef名称,是标识符的(唯一)声明。
从C ++标准3.1(2):
声明是一个定义,除非它声明了一个没有指定函数体的函数,它包含了extern说明符或一个链接规范,既没有初始化也没有函数体,它声明了一个类声明中的一个静态数据成员,它是一个类名声明,或者是typedef声明,using声明或using-directive。
那么有一些例子。
有趣的是(或者不,但是我有点惊讶), typedef int myint;
是C99中的一个定义,但只是C ++中的一个声明。
来自wiki.answers.com:
术语声明意味着(在C中)告诉编译器关于types,大小和函数声明的情况,任何variables的参数types和大小,或者程序中用户定义的types或函数。 在声明的情况下,在内存中没有空间保留任何variables。 但是编译器知道在创build这种types的variables的情况下需要预留多less空间。
例如,以下是所有的声明:
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;
看答案 。
C ++ 11更新
因为我没有看到与C ++ 11相关的答案。
声明是一个定义,除非它声明了一个/ n:
- 不透明枚举 –
enum X : int;
- 模板参数 – 模板中的T
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 ++ 03inheritance的附加子句:
- 函数声明 – 添加
int add(int x, int y);
- 包含声明或链接说明符的
extern int a;
说明符 –extern int a;
或extern "C" { ... };
- 一个类中的静态数据成员 –
class C { static int x; };
class 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
定义是指实际function书写和声明意味着简单的声明function,例如
void myfunction(); //this is simple declaration
和
void myfunction() { some statement; }
这是函数myfunction的定义
宣言 :
int a; // this declares the variable 'a' which is of type 'int'
因此,声明将variables与一个types相关联。
以下是一些声明的例子。
int a; float b; double c;
现在函数声明:
int fun(int a,int b);
注意函数末尾的分号,所以它只是一个声明。 编译器知道在程序的某个地方,函数将被定义为该原型。 现在,如果编译器得到一个像这样的函数调用
int b=fun(x,y,z);
编译器会抛出一个错误,说没有这样的函数。 因为它没有任何function的原型。
注意两个程序之间的区别。
计划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); }
因为函数调用在定义之前是很重要的,所以编译器必须知道是否有这样的函数。 所以我们声明将通知编译器的函数。
定义:
定义一个函数的这部分称为定义。 它说什么在function里面做。
void print(int a) { printf("%d",a); }
现在与变数。
int a; //declaration a=10; //definition
有些时候,声明和定义被分组到一个这样的单个语句中。
int a=10;
你不能用最通常的术语来说明一个声明是一个没有分配存储的标识符,而一个定义实际上是从一个声明的标识符中分配存储空间的吗?
一个有趣的想法 – 模板不能分配存储,直到类或函数与types信息链接。 那么模板标识符是一个声明还是定义? 它应该是一个声明,因为没有分配存储空间,而且您只是“模拟”模板类或函数。
经验法则:
-
声明告诉编译器如何解释内存中variables的数据。 这是每个访问都需要的。
-
定义保留内存以使variables存在。 这在第一次访问之前必须发生一次。
在这里find类似的答案: C中的技术面试问题
声明为程序提供了一个名称; 定义提供了对程序内实体(例如types,实例和函数)的唯一描述。 声明可以在给定的范围内重复,它在给定的范围内引入一个名称。
声明是一个定义,除非
- 宣言声明了一个没有指定其主体的函数,
- 声明包含一个extern说明符并且没有初始值或函数体,
- 声明是没有类定义的静态类数据成员的声明,
- 声明是一个类名的定义,
定义是一个声明,除非:
- 定义定义了一个静态类数据成员,
- 定义定义了一个非内联成员函数。
声明是指将variables的名称和types赋予variables(如果是variables声明),例如:
int i;
或者给名称,返回types和参数types添加一个没有主体的函数(在函数声明的情况下)
例如:
int max(int, int);
而定义意味着赋值给一个variables(在variables定义的情况下)。 例如:
i = 20;
或者向函数提供/添加主体(function)称为函数定义。
例如:
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; }
在上面的例子中,我们定义并声明了variablesi和函数max()
这听起来很俗气,但这是我能够把这些条款保持在我脑海中的最好方式:
宣言:图片托马斯·杰斐逊发表演讲……“我特此声明,这个FOO存在于这个源代码中!
定义:画一本字典,你正在查找Foo,它实际上是什么意思。
为了理解声明和定义的区别,我们需要看到汇编代码:
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)
声明没有一个等同的指令,因为它不是要执行的东西。
而且,声明告诉编译器variables的范围。
我们可以说声明是编译器用来build立variables的正确使用和某个内存属于某个variables多长时间的信息。
声明在程序中引入了一个名称; 定义提供了一个实体的唯一描述(例如types,实例和函数)。 声明可以在给定的范围内重复,它在给定的范围内引入一个名称。 C ++程序中使用的每个对象,函数或类都必须有一个定义。 声明是一个定义,除非:
* it declares a function without specifying its body, * it contains an extern specifier and no initializer or function body, * it is the declaration of a static class data member without a class definition, * it is a class name definition, * it is a typedef declaration.
定义是一个声明,除非:
* it defines a static class data member, * it defines a non-inline member function.
声明和用函数定义之间的区别:函数的原型声明声明它,即告诉编译器关于函数 – 它的名字,返回types,参数的数量和types。 函数头,后面是函数的主体,定义函数 – 给出执行函数操作的步骤的细节。
防爆。
码:
//Declare int foo(int); //Define int foo(int){ ... }
关于variables:对于自动variables和寄存器variables,定义和声明之间没有区别。 声明自动variables或寄存器variables的过程定义variables名称并分配适当的内存。
但是,对于外部variables:因为variables的内存必须只分配一次,以确保对variables的访问总是引用同一个单元格。 所有的variables必须定义一次而且只能定义一次。
如果一个外部variables被用在除了被定义的文件之外的文件中,则需要一种机制来将这种用途与为其分配的唯一定义的外部variables单元“连接”。 这个连接不同文件中相同外部variables的引用的过程被称为parsing引用。
它可以用任何函数外的声明语句来定义和声明,没有存储类说明符。 这样的声明为variables分配内存。 声明语句也可以用来在声明的开始处声明一个带有extern存储类说明符的variables名。 这样的声明指定variables是在其他地方定义的,也就是说这个variables的内存被分配在另一个文件中。 因此,如果使用关键字extern声明,则可以访问除定义文件之外的文件中的外部variables; 没有新的内存分配。 这样的声明告诉编译器该variables是在其他地方定义的,并且代码是在外部variables未被parsing的情况下编译的。 在链接过程中parsing对外部variables的引用。
防爆。
码
//file1.c extern char stack[10]; extern int stkptr; .... //file2.c char stack[10]; int stkptr; ....
这些声明告诉编译器,variablesstack []和stkptr是在别处定义的,通常在其他文件中定义。 如果省略关键字extern,则会将这些variables视为新的variables,并为其分配内存。 请记住,只有在声明中使用关键字extern时,才能访问在另一个文件中定义的相同外部variables。
声明和定义的概念将在您使用外部存储类时形成一个陷阱,因为您的定义将位于其他位置,并且在本地代码文件(页面)中声明该variables。 C和C ++之间的一个区别是,在C中,声明通常在函数或代码页的开始处完成。 在C ++中不是这样的。 你可以在你select的地方申报。
我最喜欢的例子是“int Num = 5”这里你的variables是1.定义为int 2.声明为Num和3.实例化为五。 我们
- 定义一个对象的types,可以是内build的,也可以是一个类或结构体。
- 声明一个对象的名字,所以声明了一个包含variables,函数等的名字。
一个类或结构允许你改变对象在以后使用时如何定义。 例如
- 我们可以声明一个不明确定义的异构variables或数组。
- 在C ++中使用偏移量可以定义一个没有声明名称的对象。
当我们学习编程时,这两个术语经常混淆,因为我们经常同时做两个。
每当我们在main函数之后编写函数时,编译器就会因为调用函数时对函数没有任何想法而产生错误。 如果我们提供函数的原型声明,那么我们的编译器将不会查找定义。
int sum(int,int); main() { int res = sum(10,20); } int sum(int n1,int n2) { return(n1+n2); }
在上面的例子中,第一行被称为函数声明。
int sum(int,int);
声明variables
每当我们写声明语句,那么内存将不会被分配给variables。 variables声明将随机指定内存位置。
int ivar; float fvar;
variables声明Vs定义微分参数
A.空间预订:
每当我们声明一个variables,那么空间将不会被保留给variables。 每当我们声明一个variables,编译器就不会查找其他细节,如variables的定义。
声明是编写实际内存不被分配的代码的方便方式。
struct book { int pages; float price; char *bname; };
在上面的声明中没有分配内存。 每当我们定义一个variables,那么内存将被分配给variables。
struct book b1;
B.它做了什么?
- 声明将标识标识符的数据types。
- Definition of the variable will assign some value to it.
A declaration presents a symbol name to the compiler. A definition is a declaration that allocates space for the symbol.
int f(int x); // function declaration (I know f exists) int f(int x) { return 2*x; } // declaration and definition
A variable is declared when the compiler is informed that a variable exists (and this is its type); it does not allocate the storage for the variable at that point.
A variable is defined when the compiler allocates the storage for the variable.