C语言程序设计课件1第4章 C++程序的结构_第1页
C语言程序设计课件1第4章 C++程序的结构_第2页
C语言程序设计课件1第4章 C++程序的结构_第3页
C语言程序设计课件1第4章 C++程序的结构_第4页
C语言程序设计课件1第4章 C++程序的结构_第5页
已阅读5页,还剩30页未读 继续免费阅读

下载本文档

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

文档简介

第4章

C++程序的结构

1.区分各种变量的类型以及在内存中的存放2.理解局部静态变量的双重特征3.区分各种类型标识符以及其作用域与可见性4.掌握预处理命令的常用用途5.理解名字空间的概念与使用方法学习目标

4.1变量的类型

intg=100000;intsum(intx,inty){intsum=0;for(inti=x;i<=y;i++)sum=sum+i;returnsum;}intmain(){intx=1,y=100;cout<<sum(x,y)+g<<endl;return0;}1234567891011121314g为全局变量sum()中

x、y为局部变量main()中的

x、y为局部变量

除了按数据类型区分变量外,根据变量定义的位置,可以把变量分成全局变量(globalvariable)与局部变量(localvariable)。全局变量是指定义在函数体外部的变量,它能被所有函数使用。局部变量是指定义在函数或复合语句中定义的变量,只能在函数或复合语句中使用。4.1.2变量的存储类型

C++中变量有auto、extern、register、static四种存储类型。1.auto说明符

auto说明符说明定义的是一个局部变量。函数中的局部变量,如果不用关键字static加以声明,编译系统对它们是动态分配存储空间的。局部变量默认存储类型为auto,所以在程序中很少使用auto说明符说明。

2.register说明符为提高执行效率,C++允许将局部变量的值存放在CPU的寄存器中,需要时直接从寄存器中取出参加运算,不必到内存中去存取,这种类型的局部变量称为寄存器变量,用关键字register声明。在程序中定义寄存器变量对编译系统只是建议性的,而不是强制性的。3.extern说明符如果一个完整的计算机程序很大,分成多个模块,放在不同的文件中,分开编译成目标文件,最后连接成一个完整的可执行代码。对于所有模块共同使用的全局变量,如果在所有的模块中都定义,在连接时就会出错。解决办法是只在一个模块中定义全局变量,在其他模块中用extern说明这是一个“外来”的全局变量。/****************************p4_1_p.cpp**extern型变量的使用****************************/#include<iostream>usingnamespacestd;externvoidp1dispG();externvoidp2dispG();externvoidp2dispg();intG=0,g=0;intmain(){p1dispG();p2dispG();p2dispg();cout<<"inpG="<<G<<endl;cout<<"inpg="<<g<<endl;return0;}12345678910111213141516171819//p4_1_p1.cpp#include<iostream>usingnamespacestd;externintG;voidp1dispG(){ G=11;cout<<"inp1G="<<G<<endl;}123456789//p4_1_p2.cpp#include<iostream>usingnamespacestd;externintG;externintg;voidp2dispG(){G=22;cout<<"inp2G="<<G<<endl;}voidp2dispg(){g=33;cout<<"inp2g="<<g<<endl;}123456789101112131415运行结果:inp1G=11inp2G=22inp2g=33inpG=22inpg=334.1.2变量的存储类型

4.static说明符

static说明符用于在定义变量时将变量声明为成static(静态)变量。其格式为:

static可用来声明全局静态变量和局部静态变量。当声明全局静态变量时,全局静态变量只能供本模块使用,不能被其它模块再声明为extern变量。例如:将程序p4_1_p.cpp中的全局变量声明为:

staticintG=0;

那么在其他模块中就不能声明为:externintG;//错误,G已经是一个static变量;

static数据类型变量名=初值;

注意:

当一个局部变量声明为static变量,它既具有局部变量的性质:只能在函数体局部存取;又具有全局变量的性质:函数多次进入,变量的值只初始化一次。因此静态局部变量实质是一个供函数局部存取的全局变量。/***********************************p4_2.cpp**静态局部变量的使用***********************************/#include<iostream>usingnamespacestd;voidfun()

{staticintn=0;intm=0;n++;m++;cout<<"m="<<m<<",n="<<n<<endl;}intmain(){for(inti=0;i<4;i++)fun();return0;}

1234567891011121314151617181920

运行结果:m=1n=1m=1n=2m=1n=3m=1n=4

4.1.2变量的存储类型

静态局部变量n只是在函数fun()第一次进入时初始化0,随后每次进入不再赋初值

4.1.3变量在内存中的存储

当一个程序准备运行时,操作系统会为程序分配一块内存空间,C++程序的内存通常被分为四个区: ①全局数据区(dataarea) ②代码区(codearea) ③栈区(stackarea) ④堆区(heaparea)

全局变量、静态变量、字符串常量和常变量存放在全局数据区;所有的函数和代码存放在代码区;为运行函数而分配的函数参数、局部变量和返回地址存放在栈区;动态分配内存在堆区。

变量的生存期为从产生到消失的时期。全局变量、静态变量、常变量生存周期为整个程序的生存周期,因此称为静态生存期;局部变量的生存周期起于函数调用,结束于函数调用结束,其生存期是动态的,因此称为动态生存期。注意:

堆空间不是系统为程序自动分配的,它是程序执行过程中由new语句为变量分配的。即使指向堆空间的指针变量消失,new语句分配的空间也不会消失。new语句分配的空间由delete语句释放。

/***************************************p4_3.cpp**显示各类变量的内存分配****************************************/#include<iostream>usingnamespacestd;intk=300;constinti=100;#definen10constintj=200;voidfun(inti=1,intj=2){

const

intk=3;

staticintl=0;char*p=newchar[n+1];for(intm=0;m<n;m++)p[m]='A'+m;p[m]='\0';cout<<"Adddressofparametervariable:"<<endl;

12345678910111213141516171819

4.1.3变量在内存中的存储

常变量(常量)、全局变量、局部静态变量、字符串常量在程序运行前在数据区进行分配。并且依次按照常变量、全局变量、局部静态变量的顺序从低地址向高地址分配。常变量按定义的先后次序分配,全局变量、局部变量也一样。

cout<<"&i="<<&i<<"\t"<<"&j="<<&j<<endl;cout<<"Adddressoflocalvariable:"<<endl;cout<<"&k="<<&k<<"\t"<<"&p="<<&p<<"\t"<<"&m="<<&m<<endl;cout<<"Adddressofstaticlocalvariable:"<<endl;cout<<"&l="<<&l<<endl;cout<<"Addressofheap:"<<(void*)p<<endl;cout<<"beforedeletep="<<p<<endl;

delete[]p;cout<<"afterdelete:"<<(void*)p<<endl;cout<<"p="<<p<<endl;}intmain(){L1: fun();L2: cout<<"Adddressofglobalvariable:"<<endl; cout<<"&i="<<&i<<"\t"<<"&j="<<&j<<"\t"<<"&k="<<&k<<endl;cout<<"Addressoffunction:"<<endl; cout<<"&fun="<<&fun<<"\t"<<"&main="<<&main<<endl;

return0;}

2021222324252627282930313233343536373940运行结果:Addressofparametervariable:&i=0013FF2C &j=0013FF30Addressoflocalvariable:&k=0013FF20 &p=0013FF1C &m=0013FF18Addressofstaticlocalvariable:&l=0047773CAddressofheap:00480410 //释放堆空间前指针的值beforedeletep=ABCDEFGHIJ //释放堆空间前堆中的内容afterdelete:00480410 //释放堆空间后指针的值不变p=葺葺葺葺葺葺葺?//释放堆空间后,堆中的内容无意义Addressofglobalvariable:&i=0046C01C &j=0046C020 &k=00474DC0Addressoffunction:&fun=004010A0 &main=0040123F4.1.2变量的存储类型

4.1.2变量的存储类型解释

:当函数被调用时才为函数的形参、返回代码地址、局部变量分配空间。由于空间在栈中,所以从栈底开始依次按形参、返回代码地址、局部变量的顺序从高地址向低地址分配。其中,在分配形参地址时,按从右向左的顺序;分配各局部变量的地址时,按定义的先后次序。当函数调用结束,该函数占用的栈空间收回。收回的顺序(即各变量消失的顺序)与分配的顺序相反。程序运行时,系统为各个函数的执行代码在代码段中分配空间,然后将代码调入内存。各函数在代码区的排列次序按函数定义的先后次序。程序运行结束,程序占用的代码段空间收回。全局常变量、全局变量、局部静态变量、字符串常量在程序运行前在全局数据区进行分配。并且依次按照全局常变量、全局变量、局部静态变量的顺序从低地址向高地址分配,其中各类变量按定义的先后次序分配。当程序运行结束后,各全局变量的空间被系统收回,收回的顺序与分配的顺序相同,即:先分配先收回。堆空间不是系统为程序自动分配的,它是程序执行过程中由new语句为变量分配的。即使指向堆空间的指针变量消失,new语句分配的空间也不会消失。New语句分配的空间由delete语句释放。变量的生存期为从产生到消失的时期。全局变量、静态变量、常变量生存周期为整个程序的生存周期,因此称为静态生存期;局部变量的生存周期起于函数调用,结束于函数调用结束,其生存期是动态的,因此称为动态生存期。4.2标识符的作用域与可见性

标识符的作用域是标识符在程序源代码中的有效范围,从小到大分为函数原型作用域、块作用域(局部作用域),文件作用域(全局作用域)。1.函数原型作用域函数原型作用域是C++程序中最小作用域。函数原型声明时形式参数的作用范围就是函数原型的作用域。例如:有下列函数声明:

fun(inti,intj);

标识符i、j的作用域为函数原型,即函数fun形参的两个括号之间,在其他地方不能引用这些标识符。2.块作用域所谓的块就是用{}括起来的一段程序,在块中定义的标识符,作用域从声明处开始,一直到块的大括号为止。其中有下列情况属于一个块:

(1)

函数的形参与函数体属于一个块;

(2)for()语句中,括号()中属于一个块;/***************************************p4_4.cpp**标识符作用域****************************************/#include<iostream>usingnamespacestd;inti=100,j=200;voidfun(inti=2){cout<<"L2:i="<<i<<endl;{inti=3;cout<<"L3:i="<<i<<endl;

{for(inti=4;i<5;cout<<"L6:i="<<i<<endl,i++)1234567891011121314154.2标识符的作用域与可见性i=5i=4i=3i=2

{}}{cout<<"L4:i="<<i<<endl;inti=5 i++;cout<<"L5:i="<<i<<endl;}}}}intmain(){fun();return0;}1617181920212223242526272829运行结果:L2:i=2L3:i=3L4:i=4L5:i=6L6:i=44.2标识符的作用域与可见性4.2标识符的作用域与可见性

4.可见性程序运行到某一处,能够引用到的标识符,称为该处可见到的标识符。可见性表示从某处能看到什么。可见性的一般规则是:

(1)

内层可以看到外层定义的各种标识符。

(2)

如果内、外层块定义的标识符同名,实质代表不同的实体,内层只能看到与之最近的标识符,相当于外层标识符被内层同名的标识符隐藏了。

(3)

内层标识符的作用域不能覆盖(作用)到外层,所以外层看不到内层的标识符。

(4)

同层中,后面语句定义的标识符作用域不能作用到前面语句和块。因此,前面语句看不到后面语句定义的标识符。

(5)

作用域作用的方向为从外向内、从前向后;可见性的方向则从内向外、从后向前。4.3程序的文件结构与编译预处理命令

一个高级语言源程序在计算机上运行,必须先用编译程序将其翻译为机器语言。在编译之前还要做某些预处理工作,如去掉注释,变换格式等。

C++源程序中以#开头、以换行符结尾的行称为预处理命令。预处理命令不是C++语言的语法成分,在编译前由预处理器执行,在目标程序中,不含预处理指令对应的机器码。因此,预处理命令不以分号结尾。

许多预处理命令更适合C程序员,但为了处理C遗留的代码,C++编程者也应该熟悉预处理命令。

4.3程序的文件结构与编译预处理命令

4.3.1.文件包括#include命令

文件包含是指在一个C++源程序中通过#include命令将另一个文件(通常是.h、.c或.cpp为后缀的文件)的全部内容包含进来。

文件包含处理命令的一般格式为:

编译时预编译器将被包含文件的内容插入到源程序中#include命令的位置,以形成新的源程序。

#include<被包含文件名>或

#include"被包含文件名"/******************************p4_5.cpp**主程序******************************/#include<iostream>usingnamespacestd;#include"mymath.h"intmain(){cout<<max(5,6)<<endl;return0;}1234567891011//********************************mymath.h**一些自定义的数学函数*********************************/#include<iostream>usingnamespacestd;intmax(intx,inty){returnx>y?x:y;}12345678910#include<iostream>usingnamespacestd;#include<iostream>usingnamespacestd;intmax(intx,inty){returnx>y?x:y;}intmain(){cout<<max(5,6)<<endl;return0;}123456789101112

对主程序p4_5.cpp编译时预处理程序执行#include"mymath.h",将p4_5.cpp变成:

4.3程序的文件结构与编译预处理命令

4.3.2.不带参数的宏定义

宏定义分为两种:不带参数的宏定义和带参数的宏定义。

#define命令定义一个标识符来代表一个字符串(表达式),在源程序中发现该标识符时,都用该字符串替换,以形成新的源程序。这种标识符称为宏名(macroname),将程序中出现的与宏名相同的标识符替换为字符串的过程称为宏替换或宏代换(macrosubstitulition)。不带参数的宏定义的一般形式是:

其中:#define是宏定义命令名称。标识符(宏名)被定义用来代表后面的单词串。单词串是宏的内容文本,也称为宏体,可以是任意以回车换行结尾的文字。单词串一般不用分号结尾。#define标识符单词串

源程序#include<iostream>usingnamespacestd;#defineSIZE10#defineNEWLINE"\n"#defineTAB"\t"intmain(){inta[SIZE],i;for(i=0;i<SIZE;i++)cout<<a[i]<<TAB;cout<<NEWLINE;return0;}12345678910111213预编译处理(宏替换)后的源程序#include<iostream>usingnamespacestd;#defineSIZE10#defineNEWLINE"\n"#defineTAB"\t"intmain(){inta[10],i;for(i=0;i<10;i++)cout<<a[i]<<"\t";cout<<"\n";return0;}12345678910111213注意:

宏替换时仅仅是将源程序中与宏名相同的标识符替换成宏的内容文本,并不对宏的内容文本做任何处理。4.3程序的文件结构与编译预处理命令

宏定义时,要注意以下几点:程序员通常用大写字母来定义宏名,以便与变量名区别。这种习惯帮助读者迅速识别发生宏替换的位置。同时最好把所有宏定义放在文件的最前面或另一个单独的文件中,不要把宏定义分散在文件的多个位置。宏定义时,如果单词串太长,需要写多行,可以在行尾使用反斜线\续行符。例如:

#defineLONG_STRING"thisisaverylongstringthatis\usedasanexample"双引号包括在替代的内容之内。宏名的作用域是从#define定义之后直到该宏定义所在文件结束,但通常把#define宏定义放在源程序文件的开头部分。如果需要终止宏的作用域,可以使用#undef命令,其一般格式为:宏定义可以嵌套定义、被重复定义,但不能递归定义。程序中字符串常量即双引号中的字符,不作为宏进行宏替换操作。在定义宏时,如果宏是一个表达式,那么一定要将这个表达式用()括起来,否则可能会引起非预期的结果。

#undef标识符

4.3程序的文件结构与编译预处理命令4.3.3.带参数的宏定义

#define还有一个重要的功能:定义带参数的宏。这样的宏因为定义成一个函数调用的形式,也被称为类函数宏。在C++中由于可以使用函数模板,这种用宏定义的函数模板被取代。

带参数的宏定义的一般形式为

参数表由一个或多个参数构成,参数只有参数名,没有数据类型符,参数之间用逗号隔开,参数名必须是合法的标识符。参数列表类似函数的形参表。预编译器首先将实参按参数列表中参数对应的顺序,取代内容文本中的参数;再将这个宏的实际内容文本替换源程序中的宏标识符:#define标识符(参数列表)单词串

源程序#defineMAX(x,y)(((x)>(y))?(x):(y))voidmain(){floata=-2.5,b=-3.2;cout<<MAX(a,b)<<endl;}123456预编译处理(宏替换)后的新源程序

intmain(){floata=2.5,b=-3.2;cout<<(((a)>(b))?(a):(b))<<endl;return0;}1234567说明:

预编译器在处理上面源程序中的MAX(a,b)时,首先将MAX(x,y)的宏内容文本中的x替换成a,将y替换成b,形成新的宏内容是:(((a)>(b))?(a):(b)),然后将MAX(a,b)替换成(((a)>(b))?(a):(b))。4.3程序的文件结构与编译预处理命令4.3.4.条件编译

一般情况下,源程序中所有的语句都参加编译,但有时也希望根据一定的条件去编译源文件的不同部分,这就是条件编译。条件编译使得同一源程序在不同的编译条件下得到不同的目标代码。条件编译有几种常用的形式,现分别介绍如下:

(1)#if~#endif形式

#if~

#endif形式的条件编译的格式为:

其中:elif是elseif的缩写,但不可写成elseif。#elif和#else可以省略,但#endif不能省略,它是#if命令的结尾。#elif命令可以有多个。if后面的条件必须是一个常量表达式,通常会用到宏名,条件可以不加括号()

#if条件1

程序段1#elif条件2

程序段2...#else

程序段n#endif

源程序#defineUSA0#defineCHINA1#defineENGLAND2#defineACTIVE_COUNTRYUSA#ifACTIVE_COUNTRY==USAchar*currency="dollar";

#elifACTIVE_COUNTRY==ENGLANDchar*currency="pound";#elsechar*currency="yuan";#endifintmain(){floatmoney;cin>>money;cout<<money<<currency<<endl;return0;}12345678910111213141516171819预编译处理后的新源程序

char*currency="dollar";

intmain(){floatmoney;cin>>money;cout<<money<<currency<<endl;return0;}123456789101112131415161718注意:#if和#elif常常与defined命令配合使用4.3程序的文件结构与编译预处理命令(2)#ifdef~

#endif

#ifdef~#endif形式的条件编译的格式为:其中:#else可以省略,#endif不能省略,它是#if命令的结尾。在#ifdef和#else之间可以加多个#elif命令。“#ifdef宏名”的含义是判断是否定义了宏,它等价于“#ifdefined(宏名)”。其作用是:如果宏名已被#define行定义,则编译程序段1,否则编译程序段2。

#ifdef宏名

程序段1#else

程序段2#endif4.3程序的文件结构与编译预处理命令(3)#ifndef~

#endif

#ifndef~#endif形式的条件编译的格式为:条件编译呢主要用于下列几种场合:

(1)

避免重复包含引起变量函数的重复定义引起的冲突。如果某个程序的头文件中已定义了某个常量,用条件编译进行判断后,就不再重新定义该常量,避免造成不一致;如果在头文件中如果包含某个模块,用条件编译进行判断后,就可不再重复包含此模块,避免变量、函数重名冲突。

#ifndef宏名

程序段1#else

程序段2#endif注意:

与第2种形式的区别仅在于:如果宏名没有被#define定义,则编译程序段1,否则编译程序段2。

源程序/****************************mymath.h*****************************/#include<iostream>usingnamespacestd;#defineINTMAXintmax(intx,inty){returnx>y?x:y;}/************************主程序************************/#include<iostream>usingnamespacestd;#include"mymath.h"#ifndefINTMAXintmax(intx,inty){returnx>y?x:y;}#endifintmain(){cout<<max(5,6)<<endl;return0;}123456789101112131415161718192021222324252627预编译处理后的新源程序

#include<iostream>usingnamespacestd;#include<iostream>usingnamespacestd;#defineINTMAXintmax(intx,inty){returnx>y?x:y;}intmain(){cout<<max(5,6)<<endl;return0;}

解释:主程序中定义的intmax(int,int)在预处理时被过滤掉,没有进入编译,避免了与mymath.h中的intmax(int,int)冲突。12345678910111213144.3程序的文件结构与编译预处理命令

(2)便于程序的移植。如在PC机上,最常用的C++有BC++、VC++、LinuxGNUC++,三者在实现上有一些不同之处,如果我们希望自己的源程序能够适应这种差异,可以在有差异的地方插入选择判断:#ifdefVC++…//visualC++独有的内容

#endif#ifdefBC++…//BorlandC++独有的内容

#endif#ifdefGC++…//LinuxGnuC++独有的内容

#endif

如果希望这个程序在BorlandC++环境下编译运行,可在程序的前面写上:

#defineBC++

这样一个源程序只要修改一句就可以适应三种C编译,商业软件经常就是这样编写的。商业软件的版本中经常出现的单机版、网络版,其实网络版只是在单机版的基础上增加了相应的一些网络功能,功能上大体相同,所以在同一软件程序中将单机版的功能和网络版的功能通过条件编译就可得到相应的版本。4.4名字空间

一个软件往往由多个模块(组件)组成,这些模块由不同程序员(软件商)提供,不同模块可能使用了相同的标识符。简单说就是同一个名字在不同模块中代表不同的事物。当这些模块用到同一个程序中,同名标识符就引起冲突。C++提供名字空间(namespace)

将相同的名字放在不同空间中来防止命名冲突。

定义一个名字空间的格式如下:其中:namespace为关键字。名称为名字空间标识符。成员为函数、变量、常量、自定义类型等。

namespace名称

{

成员;

}4.4名字空间例如:一个名为TsingHua的软件公司为自己的组件建立了一个名字空间。将它存入头文件:TsingHua.h/*************TsingHua.h**********/namespaceTsingHua{intyear=2009;charname[]="TsingHuaSoftware";ShowName(){

cout<<name<<""<<year<<endl;}}4.4名字空间(1)个别使用声明方式,格式如下:

其中:::为作用域分辨符。成员使用形式包括函数调用式、变量名、常量名、类型名等名字空间名::成员使用形式/******************************p4_6.cpp**名字空间

温馨提示

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

评论

0/150

提交评论