C语言和C++在声明和定义之间的区别是什么?

更新时间:2023-05-04 16:21:20 阅读: 评论:0

声明是告诉编译器有一个变量或函数,并标明是什么类型的.
而定义,是明确告诉编译器,这个变量或函数的值和实现方式.
一般来讲,声明变量并不会占用存储空间,而定义变量会占用存储空间.声明函数只提供函数的格式,定义函数则提供函数的实现代码.
当然,很多时候,声明和定义是合并在一起的,这样的声明称为为定义声明.
举例来讲:
int  test=1;//这是一个定义声明,term被分配了内存空间
extern int test;//这是一个声明而不是定义,它告诉编译器要使用先前定义的变量test.称为引用声明.
函数的定义和声明一般是分开的,比较清楚,也有将函数的定义和声明合并.
c++primer第四版中,53页的习题2.18中问到,
extern std::string name;是属于声明还是定义?
答案说的是声明。
不过我认为,string类中有默认构造函数,如果该语句在函数外,那么则会自动获得储存空间,将name定义为空字符串,不知各位有何高见?
我刚才做了一个实验:
//a.cpp
#include <string>
std::string i;
//b.cpp
#include <iostream>
#include <string>
extern std::string i;
int main ()
{
std::cout << i << std::endl;
}
运行结果为:
(上面是一空行:由于末尾用了endl)
如果没有a.cpp这个文件,运行出错
这是一对实验(有没有a.cpp文件)
可得出这样的结论:
a.cpp中std::string i;是定义(本来就是)
b渭南特产 .cpp中extern std::string i; 则是声明(你的问题)
先别急,这只是实验结果。我们还需要理论依据,那么下面就试着解释一下:
下面是另一个例子(也是第二个实验)
你应该知道内置类型(int等)如果作为全局变量时,定义的同时也就初始化了(一个内置类型有其对应的默认值;如果你将自定义类型也看成内置类型,那么就等于说,一个自定义类型有其对应的默认值(由默认构造函数完成))。如
//c.cpp
int i;
//d.cpp
#include <iostream>
extern int i;
int main ()
{
std::cout << i << std::endl;
}
那么在这里int i; 就是定义(并且初始化了i,初始值为0)
因而输出结果为
0
//这个实验的运行结果符合理论依据。
(我使用的编译器是visual studio 2008)
从这两个实验来看:(将两个“默认”等同起来)
int i;
std::string i;
都可以作为初始化i的定义性语句。
那么
extern int i;
extern std::string i;
也具有定义行为(这个很明显与extern的用法相悖)
在以上两个试验中:int 与 string 有“区别”吗?所有的代码都一样只不过把std::string 换成了 int而已!如果你能解释int的情况,那么我想std::string问题也就迎刃而解了!
于是我个人总结出一个结论:
不论是内置类型还是自定义类型,前面有extern关键字,如果没有显式
地初始化参数(即就是自定义类型的默认构造函数被extern屏蔽了,不起初始化的作用),那么就按声明对待。
可能我的描述很混乱,希望你好好看看上面的例子,就明白了!
注意:从这里可以看出来默认构造函数的特殊性!
c++程序通常由许多文件组成,为了让多个文件访问相同的变量,c++区分了声明和定义。
变量的定义(definition)用于为变量分配存储空间,还可以为变量指定初始值。在程序中,变量有且
仅有一个定义。
声明(declaration)用于向程序表明变量的类型和名字。定义也是声明:当定义变量的时候我们声明了它的类型和名字。可以通过使用extern声明变量名而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字extern。
extern声明不是定义,也不分配存储空间。事实上它只是说明变量定义在程序的其他地方。程序中变量可以声明多次,但只能定义一次。
只有当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间。初始化式必须要有存储空间来进行初始化。如果声明有初始化式,那么它可被当作是定义,即使声明标记为extern。
任何在多文件中使用的变量都需要有与定义分离的声明。在这种情况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(而不是定义)。
说明:个人感觉好像就是extern存在声明和定义不是一回事,其它几个声明和定义就是一回事
  auto (自动类作用域在函数体内和分程序内,)
register (作用域在函数体内和分程序内)
static (内部静态类作用域在函数体内和分程序内,外部静态态作用域在定义它的类中,但其生命周期却一样)
  extern (作用域在整个程序内,包含该程序的各个文件)
    在其个文件中使用extern变量时,必须先声明,还有就是如果使用在前,定义在后时也要声明。
 在函数体外的一个变量如果不加存储内型,默认便为extern,在函数体内如果一个变量不加存储说明
   则默认为auto
{
int a = 5;//声明加定义加初始化
  int b ;//声明加定义
b = 4; //赋值
}
extern int a ;//声明
extern int i =1;//定义
附:如果一个变量被定义但没有被初始化,也没有被赋值,则存储类为静态的和extern的变量值为默认值
  (数值量为0,字符量为null),存储类为auto和register的为无效值,即值是随机的,此值不可使用,变量被赋值和有默认值称为有效值
定义与声明是C/C++基本的概念,看似很简单的东西,但区分不好往往很容易出错。C/C++中规定,使用一个对
象(非面向对象中的对象,此处对象指广泛意义上的程序元素,下同)前必须先定义该对象。所谓定义,是指在程序中描述一个对象实体;声明是指将一个名称引入一个程序。大多数情况下可以不加区分的使用它们,因为这时候定义就是声明。
当涉及到头文件与源文件时,情况就变得有些复杂了。类比于对象的声明与定义,C/C++中头文件可认为是声明,而源文件用于定义,形式上有点类似于Java的接口和实现类。作为类库提供给第三方使用时,只需提供头文件和实现的Dll即可,达到了信息隐藏效果。由于头文件不能单独编译,即不能通
过编译头文件产生符号表,因此在头文件中描述某种实现的过程是非法的。
先来看看那些情况只是声明而非定义:
1. 包含extern定义符的变量和函数;
2. 没有提供实现的函数(没有函数体);
3. 类中的静态数据成员;
4. teypedef声明;
关于声明和定义还有个区别就是在一个作用域中可以重复声明,但不能重复定义。
还是看例子吧,比较容易说明问题,在头文件(head.h)中:
#ifndef H1_H
#define H1_H
typedef char* NAME;
typedef char* NAME;    //重复声明,没问题
extern int k;
//int ilk1;  此处言楚非 为定义,非法。注,此句在VC6中是合法的,可能是VC2005现实C99标准
//extern int ilk2 = 4;  非法,
extern void outPut();//声明,合法
void outPut2(); //声明,合法
//void ilOutPut(){}  定义,非法
inline void put(){}//内敛,合法
static int i;
//static int ili2 = 4; 非法
class Radio{
public:
static int s_count;   
void ilfoo();
/*void ilfoo();重复声明,非法。 注意,类中的声明不能重复,这是声明能重复的特殊情况*/
private:
int size() const;
char getchar()  //合法
{
return 'c';
}
};
inline int Radio::size() const
{
return s_count;
}
extern Radio *ra;//合法,声明
//Radio ra; 非法, 可此句放到包含该头文件的源文件中
//void Radio::ilfoo(){}; 非法, 可此句放到包含该头文件的源文件中
//int Radio::s_count = 5;  非法, 可此句放到包含该头文件的源文件中
#endif
  “声明”向计算机介绍名字,它说,“这个名字是什么意思”。 
 “定义”为这个名字分配存储空间。
  无论涉及到变量时还是函数时含义都一样。无论在哪种情况下,编译器都在“定义”处分配不同英语 存储空间。对于变量,编译器确定这个变量占多少存储单元,并在内存中产生存放它们的空间。对于函数,编译器产生代码,并为之分配存储空间。函数的存储空间中有一个由使用不带参数表或带地址操作符的函数名产生
的指针。
  另外,定义也可以是声明。如果该编译器还没有看到过名字A,程序员定义int A,则编译器马上为这个名字分配存储地址。声明常常使用于e x t e r n关键字。如果我们只是声明变量而不是定义它,则要求使用e x t e r n。对于函数声明, e x t e r n是可选的,不带函数体的函数名连同参数表或返回值,自动地作为一个声明。
  函数原型包括关于参数类型和返回值的全部信息。int f(float,char);是一个函数原型,因为它不仅介绍f这个函数的名字,而且告诉编译器这个函数有什么样的参数和返回值,使得编译器能对参数和返回值做适当的处理。C + +要求必须写出函数原型,因为它增加了一个重要的安全层。
  下面是一些声明的例子。
//声明变量
extern int varible;
//声明函数
extern float foo(float,float);
float b;
float c;
//定义函数
float foo(float a,float b)
{
return a + b;
}
//定义整型变量
int varible;
//定义函数
int h(int x)
{
return x + 1;
}
void main(int argc, _TCHAR* argv[])
{
varible = 2;
b = 1.0;
c = 2.莫斯科会战 3;
foo(b,c);
h(varible);
}
  在函数声明时,参数名可给出也可不给出。而在定义时,它们是必需的。这在C语言中确实如此,但在C + +中并不一定。
  其实很简单,总结起来就是:声明不分配内存空间,定义则相反,定义可以是声明,但声明一定不是定义!
all but one of the following are definitions:
int a; // defines a
extern const int c = 1; // defines c
int f(in温泉的形成 t 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 nonstatic 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; } // definesN and N::d
namespace N1 = N; // defines N1
X anX; // defines anX
whereas the are just 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
在C++中,变量、对象、函数都需要声明,使用之前需要定义,本文针对初学者在此方面经常忽略但又很重要的问题给出了解释,并提供了此方面良好的编程风格。
由于大家都对局部变量的定义与使用十分熟悉,所以在此主要总结本人认为两点应该注意的问题:
1.头文件与声明的关系
2.符号常量与inline函数的声明
l        讲解
首先,头文件为所向extern 对象声明函数声明以及inline 函数定义提供了一个集中
的位置。
头文件提供了两个安全保证:第一,保证所有文件都包含同一个全局对象或函数的同一份声明;第二,如果需要修改声明,则只需改变一个头文件从而不至于再发生只修改了某一个特殊的文件中的声明。如果应用程序有很大的头文件,则使用预编译头文件而不是普通头文件可以大大降低应用程序的编译时间。
其次修真种植大户 ,头文件不应该含有非inline 函数或对象的定义。因为如果这些定义在同一程序的两个或多个文件中被包含就会产生重复定义的编译错误。
但是,常量和inline 函数却可以违反这条规则,因为常量和inline 函数可以被定义多次,而归根到底是因为编译器在编译期间要将常量和inline函数展开的原因。
. 函数
我觉得这是一种最容易理解的情况。函数的声明就是函数原型,函数的定义就是函数的具体实现。编
译器在遇到一个函数调用语句时,必须知道这个函数的原型以进行相应的类型检查,因此必须把该函数的声明(当然定义也可以,因为在此时定义隐含了声明)放在前面。
只要是比ACM题目那种一百行左右代码再大一点的项目,一般都不会把所有代码写在一个文件里。注意编译器的执行过程是分两步的,首先把每个.cpp文件(或者.c, 等等)分别编译成目标文件(通常是.o, .obj等等),然后再用链接器把目标文件组装成一个可执行文件。注意“分别”两字,也就是说,编译器必须能够不依赖其他cpp文件就可以把当前的cpp文件编译成目标文件。
我们先不考虑类等等内容,假设现在我实现了一系列逻辑上有一定关联的工具函数,为了清楚起见我把它们放在同一个util.cpp文件里,而把使用这些工具函数的其他代码放在文件main.cpp里,那么要编译main.cpp,必须知道里面用到的工具函数的原型,所以必须在main.cpp里有一份该函数的声明(只需要声明就够了,编译器可以据此生成压栈、取返回值等等汇编代码,至于函数内部的实现代码是在util.o目标文件里,等链接的时候组装在一起就可以)
然而,当有多个cpp文件都要用到这些工具函数时,如果我们每次都要在cpp文件前面附加一堆函数声明的话,就显得有点太啰嗦了。于是头文件就派上用场了。这时惯常的做法是把util里(想提供给外界使用的)函数的声明放在.h头文件里,如果另外的cpp文件要用到某个函数,只要把对应的头文件include进来即可。注意一定不要把函数的定义放在头文件里,否则的话,如果这个头文件被包含进多
个cpp文件里,在链接时就会出现函数重定义的错误。也就是说,函数可以声明多次,但只能定义一次(有一个例外下面会

本文发布于:2023-05-04 16:21:20,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/82/525193.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:声明   定义   函数   变量   文件   头文件   类型   使用
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图