C语言和C++在声明和定义之间的区别是什么?_第1页
C语言和C++在声明和定义之间的区别是什么?_第2页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、c语言和c+在声明和定义之间的区别是什么? c语言和c+在声明和定义之间的区分是什么 声明是告知编译器有一个变量或函数,并标明是什么类型的. 而定义,是明确告知编译器,这个变量或函数的值和实现方式.一般来讲,声明变量并不会占用存储空间,而定义变量会占用存储空间.声明函数只供应函数的格式,定义函数则供应函数的实现代码.当然,许多时候,声明和定义是合并在一起的,这样的声明称为为定义声明.举例来讲:int test=1;/这是一个定义声明,term被安排了内存空间extern int test;/这是一个声明而不是定义,它告知编译器要使用从前定义的变量test.称为引用声明.函数的定义和声明一般是分

2、开的,比较清晰,也有将函数的定义和声明合并.c+primer第四版中,53页的习题2.18中问到,extern std:string name;是属于声明还是定义?答案说的是声明。不过我认为,string类中有默认构造函数,假如该语句在函数外,那么则会自动获得储存空间,将name定义为空字符串,不知各位有何高见?我刚才做了一个试验:/a.cpp#include stringstd:string i;/b.cpp#include iostream#include stringextern std:string i;int main ()std:cout i std:endl;运行结果为:(上面是

3、一空行:由于末尾用了endl)假如没有a.cpp这个文件,运行出错这是一对试验(有没有a.cpp文件)可得出这样的结论:a.cpp中std:string i;是定义(原来就是)b.cpp中extern std:string i; 则是声明(你的问题)先别急,这只是试验结果。我们还需要理论依据,那么下面就试着解释一下:下面是另一个例子(也是其次个试验)你应当知道内置类型(int等)假如作为全局变量时,定义的同时也就初始化了(一个内置类型有其对应的默认值;假如你将自定义类型也看成内置类型,那么就等于说,一个自定义类型有其对应的默认值(由默认构造函数完成)。如/c.cppint i;/d.cpp#i

4、nclude iostreamextern int i;int main ()std:cout i std:endl;那么在这里int i; 就是定义(并且初始化了i,初始值为0)因而输出结果为0/这个试验的运行结果符合理论依据。(我使用的编译器是visual studio 2021)从这两个试验来看:(将两个“默认”等同起来)int i;std:string i;都可以作为初始化i的定义性语句。那么extern int i;extern std:string i;也具有定义行为(这个很明显与extern的用法相悖)在以上两个试验中:int 与 string 有“区分”吗?全部的代码都一样只不

5、过把std:string 换成了 int而已!假如你能解释int的状况,那么我想std:string问题也就迎刃而解了!于是我个人总结出一个结论:不论是内置类型还是自定义类型,前面有extern关键字,假如没有显式 c语言和c+在声明和定义之间的区分是什么 地初始化参数(即就是自定义类型的默认构造函数被extern屏蔽了,不起初始化的作用),那么就按声明对待。 可能我的描述很混乱,盼望你好好看看上面的例子,就明白了!留意:从这里可以看出来默认构造函数的特别性!c+程序通常由很多文件组成,为了让多个文件访问相同的变量,c+区分了声明和定义。变量的定义(definition)用于为变量安排存储空间

6、,还可以为变量指定初始值。在程序中,变量有且仅有一个定义。声明(declaration)用于向程序表明变量的类型和名字。定义也是声明:当定义变量的时候我们声明白它的类型和名字。可以通过使用extern声明变量名而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字extern。extern声明不是定义,也不安排存储空间。事实上它只是说明变量定义在程序的其他地方。程序中变量可以声明多次,但只能定义一次。只有当声明也是定义时,声明才可以有初始化式,由于只有定义才安排存储空间。初始化式必需要有存储空间来进行初始化。假如声明有初始化式,那么它可被当作是定义,即使声明标记为extern。任

7、何在多文件中使用的变量都需要有与定义分别的声明。在这种状况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(而不是定义)。说明:个人感觉似乎就是extern存在声明和定义不是一回事,其它几个声明和定义就是一回事auto(自动类作用域在函数体内和分程序内,)register(作用域在函数体内和分程序内)static (内部静态类作用域在函数体内和分程序内,外部静态态作用域在定义它的类中,但其生命周期却一样)extern (作用域在整个程序内,包含该程序的各个文件)在其个文件中使用extern变量时,必需先声明,还有就是假如使用在前,定义在后时也要声明。在函数体外的一个变量假如不

8、加存储内型,默认便为extern,在函数体内假如一个变量不加存储说明则默认为autoint a = 5;/声明加定义加初始化 int b ;/声明加定义b = 4; /赋值extern int a ;/声明extern int i =1;/定义附:假如一个变量被定义但没有被初始化,也没有被赋值,则存储类为静态的和extern的变量值为默认值(数值量为0,字符量为null),存储类为auto和register的为无效值,即值是随机的,此值不行使用,变量被赋值和有默认值称为有效值定义与声明是c/c+基本的概念,看似很简洁的东西,但区分不好往往很简单出错。c/c+中规定,使用一个对 c语言和c+在声

9、明和定义之间的区分是什么 象(非面对对象中的对象,此处对象指广泛意义上的程序元素,下同)前必需先定义该对象。所谓定义,是指在程序中描述一个对象实体;声明是指将一个名称引入一个程序。大多数状况下可以不加区分的使用它们,由于这时候定义就是声明。 当涉及到头文件与源文件时,状况就变得有些简单了。类比于对象的声明与定义,c/c+中头文件可认为是声明,而源文件用于定义,形式上有点类似于java的接口和实现类。作为类库供应给第三方使用时,只需供应头文件和实现的dll即可,达到了信息隐蔽效果。由于头文件不能单独编译,即不能通过编译头文件产生符号表,因此在头文件中描述某种实现的过程是非法的。先来看看那些状况只

10、是声明而非定义:1. 包含extern定义符的变量和函数;2. 没有供应实现的函数(没有函数体);3. 类中的静态数据成员;4. teypedef声明;关于声明和定义还有个区分就是在一个作用域中可以重复声明,但不能重复定义。还是看例子吧,比较简单说明问题,在头文件(head.h)中:#ifndef h1_h#define h1_htypedef char* name;typedef char* name; /重复声明,没问题extern int k;/int ilk1; 此处为定义,非法。注,此句在vc6中是合法的,可能是vc2021现实c99标准/extern int ilk2 = 4; 非

11、法,extern void output();/声明,合法void output2(); /声明,合法/void iloutput() 定义,非法inline void put()/内敛,合法static int i;/static int ili2 = 4; 非法class radiopublic:static int s_count;void ilfoo();/*void ilfoo();重复声明,非法。 留意,类中的声明不能重复,这是声明能重复的特别状况*/private:int size() const;char getchar() /合法return 'c'inlin

12、e int radio:size() constreturn s_count;extern radio *ra;/合法,声明/radio ra; 非法, 可此句放到包含该头文件的源文件中/void radio:ilfoo(); 非法, 可此句放到包含该头文件的源文件中/int radio:s_count = 5; 非法, 可此句放到包含该头文件的源文件中#endif“声明”向计算机介绍名字,它说,“这个名字是什么意思”。“定义”为这个名字安排存储空间。无论涉及到变量时还是函数时含义都一样。无论在哪种状况下,编译器都在“定义”处安排存储空间。对于变量,编译器确定这个变量占多少存储单元,并在内存中

13、产生存放它们的空间。对于函数,编译器产生代码,并为之安排存储空间。函数的存储空间中有一个由使用不带参数表或带地址操作符的函数名产生 c语言和c+在声明和定义之间的区分是什么 的指针。 另外,定义也可以是声明。假如该编译器还没有看到过名字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);是一

14、个函数原型,由于它不仅介绍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, _tcha

15、r* 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 aextern const int c = 1; / defines cint f(int x) return x+a; / defines f and de

16、fines xstruct s int a; int b; ; / defines s, s:a, and s:bstruct x / defines xint x; / defines nonstatic data member xstatic int y; / declares static data member yx(): x(0) / defines a constructor of x;int x:y = 1; / defines x:yenum up, down ; / defines up and downnamespace n int d; / definesn and n:

17、dnamespace n1 = n; / defines n1x anx; / defines anxwhereas these are just declarations:extern int a; / declares aextern const int c; / declares cint f(int); / declares fstruct s; / declares stypedef int int; / declares intextern x anotherx; / declares anotherxusing n:d; / declares n:d在c+中,变量、对象、函数都需

18、要声明,使用之前需要定义,本文针对初学者在此方面常常忽视但又很重要的问题给出了解释,并供应了此方面良好的编程风格。由于大家都对局部变量的定义与使用非常熟识,所以在此主要总结本人认为两点应当留意的问题:1.头文件与声明的关系2.符号常量与inline函数的声明l 讲解首先,头文件为所向extern 对象声明函数声明以及inline 函数定义供应了一个集中 c语言和c+在声明和定义之间的区分是什么 的位置。 头文件供应了两个平安保证:第一,保证全部文件都包含同一个全局对象或函数的同一份声明;其次,假如需要修改声明,则只需转变一个头文件从而不至于再发生只修改了某一个特别的文件中的声明。假如应用程序有

19、很大的头文件,则使用预编译头文件而不是一般头文件可以大大降低应用程序的编译时间。其次,头文件不应当含有非inline 函数或对象的定义。由于假如这些定义在同一程序的两个或多个文件中被包含就会产生重复定义的编译错误。但是,常量和inline 函数却可以违反这条规章,由于常量和inline 函数可以被定义多次,而归根究竟是由于编译器在编译期间要将常量和inline函数绽开的缘由。. 函数我觉得这是一种最简单理解的状况。函数的声明就是函数原型,函数的定义就是函数的详细实现。编译器在遇到一个函数调用语句时,必需知道这个函数的原型以进行相应的类型检查,因此必需把该函数的声明(当然定义也可以,由于在此时定

20、义隐含了声明)放在前面。只要是比acm题目那种一百行左右代码再大一点的项目,一般都不会把全部代码写在一个文件里。留意编译器的执行过程是分两步的,首先把每个.cpp文件(或者.c, .cc等等)分别编译成目标文件(通常是.o, .obj等等),然后再用链接器把目标文件组装成一个可执行文件。留意“分别”两字,也就是说,编译器必需能够不依靠其他cpp文件就可以把当前的cpp文件编译成目标文件。我们先不考虑类等等内容,假设现在我实现了一系列规律上有肯定关联的工具函数,为了清晰起见我把它们放在同一个util.cpp文件里,而把使用这些工具函数的其他代码放在文件main.cpp里,那么要编译main.cp

21、p,必需知道里面用到的工具函数的原型,所以必需在main.cpp里有一份该函数的声明(只需要声明就够了,编译器可以据此生成压栈、取返回值等等汇编代码,至于函数内部的实现代码是在util.o目标文件里,等链接的时候组装在一起就可以)然而,当有多个cpp文件都要用到这些工具函数时,假如我们每次都要在cpp文件前面附加一堆函数声明的话,就显得有点太啰嗦了。于是头文件就派上用场了。这时惯常的做法是把util里(想供应给外界使用的)函数的声明放在.h头文件里,假如另外的cpp文件要用到某个函数,只要把对应的头文件include进来即可。留意肯定不要把函数的定义放在头文件里,否则的话,假如这个头文件被包含

22、进多个cpp文件里,在链接时就会消失函数重定义的错误。也就是说,函数可以声明多次,但只能定义一次(有一个例外下面会 c语言和c+在声明和定义之间的区分是什么 提到)。 另外一点是,假如不想某个文件里的函数被外面看到,应当用static修饰。应当只开放那些想公开的函数,并把想公开的函数的声明放到对应的头文件里。这里再插一句,头文件只是为了便利程序员,编译器是看不到头文件的,把include的文件插入到相应的位置之类的事情都是预处理器完成的。头文件不必有对应的.cpp文件,不必是.h扩展名,实际上它可以是任凭什么东西,只要你情愿就好,编译器根本不care。关于头文件还有一点,假如我们不当心的话,常

23、常会消失同一份头文件被包含了多次的状况(有时候可能是不行避开的)。比如我自己写了一个 roba.h,里面有一句#include string,又写了一个main.h,里面即include了roba.h,又include了string,这样 string就被包含了两次。这样就有可能消失重复定义(由于头文件里不止有函数声明,还有比如类的定义),这时我们可以用#ifndef这个东西掌握这个头文件的内容只被包含一次。常见写法如下:#ifndef _roba_h_ #define _roba_h_ /. /the body of roba.h /. #endif最终再说一下inline函数,这是一个特例

24、,inline函数的定义应当写在头文件里。由于编译器要内联这个函数,就必需知道函数的实现,不然的话是无法单独编译出一个目标文件的。对inline函数不必担忧重定义的问题,它可以看作局部于所在的源文件的。2. 变量这里主要是争论全局变量的使用,对于局部变量(即在函数内部定义的,在栈上安排空间的变量)不予考虑。全局变量的状况与前一篇说的函数的状况类似,但是更迷惑人一些。全局变量的定义是在全部函数的外面,比如下面语句:int roba;定义了一个叫做roba的int型全局变量。而全局变量的声明是这样:extern int roba;全局变量与函数一样要求,定义只能一次,声明可以多次,在使用前编译器必

25、需看到声明。假如多个源文件要共享一个全局变量,初学者常犯的错误有两种:一种是在多个文件中都写了int roba;,这样会导致链接时发生重复定义的错误;另一种是只在一个文件中写了int roba;,以为在其他的文件的文件中可以直接用,但实际上由于在其他文件中没有对这个全局变量的声明,所以会发生编译错误。正确的写法是,在某一个文件中用int roba的形式定义,而在其他用到该变量的文件中用extern int roba的形式声明。假如要把变量放到头文件里,从前面的争论可以看出,肯定不要把int roba;这样的定义放进去(由于这样的话假如这个头文件被包括进多个cpp文件就会产生重复定义),而应当写extern int roba;这样的声明,然后在某个cpp文件中定义int roba; 。与函数类似,假如我 c语言和c+在声明和定义之间的区分是什么 们不想其他的文件看到某个全局变量,也可以用static修饰它,表示它的作用域局部于此文件内。(留意我说的不是static的局部变量) 下面又开头说特例了。对于这种写法:extern int roba = 1;是被当作定义而不是声明的,尽管加了一个extern

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论