实验三-第4章函数与预处理_第1页
实验三-第4章函数与预处理_第2页
实验三-第4章函数与预处理_第3页
实验三-第4章函数与预处理_第4页
实验三-第4章函数与预处理_第5页
免费预览已结束,剩余89页可下载查看

下载本文档

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

文档简介

第4章函数与预处理概述函数的定义函数参数和函数的值函数的调用内置函数函数重载函数模板有默认参数的函数函数调用机制函数的递归调用局部变量和全局变量变量的关于变量的类别和定义函数和外部函数预处理命令4.1

概述程序通常是非常复杂而冗长的,在编写一个很长的程序时,往往将之分割成一些相对独立而且便于管理和阅读的小块程序。把实现某能的相关语句组织在一起,并给之注明相应的名称,这种形式的组合就称为函数。任何一个C++程序都是由若干函数组成的。函数按其是否为系统预定义的可分为二类:①库函数(或标准函数)。②用户自定义函数。函数按是否带有参数可分为有参函数和无参函数。在一个程序文件中可以包含若干个函数。无论把一个程序划分为多少个程序模块,只能有一个main函数。程序总是从main函数开始执行的。在程序运行过程中,由主函数调用其他函数,其他函数也可以互相调用。在实际应用的程序中,主函数写得很简单,它的作用就是调用各个函数,程序各部分的功能全部都是由各函数实现的。主函数相当于总调度,调动各函数依次实现各项功能。例在主函数中调用其他函数。//输出30个“*”#include

<iostream>using

namespacestd;void

printstar()

//定义printstar函数{cout<<"******************************"<<endl;}cout<<" e

toC++!"<<endl; //输出一行文字void

print_message()//定义print_message函数{}//调用printstar

函数//调用print_message函数//调用printstar

函数intmain(){printstar();print_message();printstar();return

0;}*函数机制一是指程序运行过程中对函数调用的数据管理和处理过程。二是指编程中函数的使用规范。它包括:;函数参数的属性和传递规则;函数返回类型的匹配与函数名字的识别原则;函数体效率的选择;函数体中数据的

权限等。4.2

函数的定义一个函数包括函数头和函数体两部分。①函数头定义函数功能和接口的全部要素,包括函数名、函数参数和函数返回值类型。②函数体则定义函数的算法实现。函数必须先定义后使用。格式:<数据类型>函数名(参数类型1

形式参数1<,参数类型2

形式参数2…>){函数体}#include<iostream>using

namespace

std;double

max(double

a,

double

b){return

(a>=b?

a:

b);}int

main(

){double

x,

y,

z;cin>>x>>y;z=max(x,

y);cout<<z<<endl;return

0;}C++中不允许函数的嵌套定义,即不允许在一个函数中定义另一个函数。4.3

函数参数和函数的值形式参数和实际参数在调用函数时,大多数情况下,函数是带参数的。主调函数和被调用函数之间有数据传递关系。在定义函数时函数名后面括号中的变量名称为形式参数(formal

parameter,简称形参),在主调函数中调用一个函数时,函数名后面括号中的参数(可以是一个表达式)称为实际参数(actualparameter,简称实参)。例调用函数时的数据传递。//定义有参函数max#include

<iostream>using

namespace

std;int

max(int

x,int

y){int

z;z=x>y?x:y;return

z;}c=max(a,b);

//调用max函数,给定实参为a,b。函数值赋给ccout<<"max="<<c<<endl;return

0;int

main(

){int

a,b,c;cout<<"please

enter

two

integer

numbers:";cin>>a>>b;}运行情况如下:please

enter

two

integer

numbers:2

3↙max=3有关形参与实参的说明:(1)在定义函数时指定的形参,在未出现函数调用时,它们并不占内存中的

单元,因此称它们是形式参数或虚拟参数,表示它们并不是实际存在的数据,只有在发生函数调用时,函数max中的形参才被分配内存单元,以便接收从实参传来的数据。在调用结束后,形参所占的内存单元也被

。(2)

实参可以是常量、变量或表达式,如max(3,a+b);但要求a和b有确定的值。以便在调用函数时将实参的值赋给形参。(3)

在定义函数时,必须在函数首部指定形参的类型。(4)实参与形参的类型应相同或赋值兼容。(5)函数参数的传递方式可分为按值传递和按传递两种!!!按值传递:将实参的值

给形参,在函数中参加运算的是形参,而实参不会发生任何改变。#include<iostream>using

namespace

std;int

fun(int

a){

++a;return

a;}int

main(

){int

x=1,y;y=fun(x);cout<<y<<endl;return

0;}11xa22y4.3.2

函数的返回值(1)函数的返回值是通过函数中的return语句获得的。return语句将被调用函数中的一个确定值带回主调函数中去。格式:return

表达式;或return(表达式);(2)函数值的类型。既然函数有返回值,这个值当然应属于某一个确定的类型,应当在定义函数时指定函数值的类型。(3)如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准,即函数类型决定返回值的类型。对数值型数据,可以自动进行类型转换。注意:对于有返回值的函数,在函数的出口处必须

用“return”语句将要返回的值返回给调用者。函数一旦执行到“return”语句便会终止执行,返回调用单元。#include<iostream>using

namespace

std;char

toColor(int

n){switch(n){case

0:

return('R');case

1:

return('G');case

2:

return('B');}}int

main(){int

a=1,b=0;cout<<"a:

"<<toColor(a)<<endl;cout<<"b:

"<<toColor(b)<<endl;return

0;}输出a:

Gb:R4.4

函数的调用函数调用的一般形式格式:函数名([实参表列])如果是调用无参函数,则“实参表列”可以没有,但括号不能省略。如果实参表列包含多个实参,则各参数间用逗号隔开。实参与形参的个数应相等,类型应匹配(相同或赋值兼容)。实参与形参按顺序对应,一对一地传递数据。但应说明,如果实参表列包括多个实参,对实参求值的顺序并不是确定的。4.4.2

函数调用的方式按函数在语句中的作用来分,可以有以下3种函数调用方式:1.函数语句把函数调用单独作为一个语句,并不要求函数带回一个值,只是要求函数完成一定的操作。如例4.1中的printstar();2.函数表达式函数出现在一个表达式中,这时要求函数带回一个确定的值以参加表达式的运算。如c=2*max(a,b);3.函数参数函数调用作为一个函数的实参。如m=max(a,max(b,c));

//max(b,c)是函数调用,其值作为外层max函数调用的一个实参4.4.3

对被调用函数的

和函数原型C++中,对函数之间的排列顺序没有固定的要求,但一定要满足先定义后使用的原则。对于标准函数,在程序开头用“#include”指令将所需头文件包含进来即可;而对于自定义函数,只要在调用之前做函数

,则无论函数定义放在什么位置,程序都能正确编译、运行。函数

也称为函数原型。格式:<数据类型>函数名(参数类型1

形式参数1<,参数类型2

形式参数2…>);…<数据类型>函数名(参数类型1

形式参数1<,参数类型2

形式参数2…>){函数体}例:#include<iostream>using

namespace

std;double

max(double

a,double

b);int

main(){double

x,y,z;cin>>x>>y;z=max(x,y);cout<<z<<endl;return

0;}double

max(double

a,

double

b){return

(a>=b?a:b);}注意:对函数的定义和不是同一件事情。定义是指对函数功能的确立,包括指定函数名、函数类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。

的作用则是把函数的名字、函数类型以及形参的个数、类型和顺序(注意,不包括函数体)通知编译系统,以便在对包含函数调用的语句进行编译时,据此对其进行对照检查(例如函数名是否正确,实参与形参的类型和个数是否一致)。4.5

内置函数函数调用需要建立栈内存环境,进行参数传递,并

产生程序执行转移,这些工作都需要一些时间开销。有些函数使用频率高,但代码却很短,程序频繁调用该函数造成执行效率降低。为了提高效率,解决方法之一是不使用函数,直接将函数代码嵌入到程序中。但这个方法的缺点是相同代码段需要重复书写,另外,程序的可读性往往也没有使用函数时好。为了协调效率和可读性之间的 ,C++提供了另法,即定义内联函数方法是在定义函数时使用修饰词inline。#include<iostream>using

namespace

std;inline

boolisNumber(char);int

main(){char

c;while(cin>>c&&c!='q'){if(isNumber(c))cout<<"digit"<<endl;elsecout<<"not

digit"<<endl;}}bool

isNumber(char

ch){return

(ch>='0'&&ch<='9')?1:0;}内联函数的调用机制与一般函数不同,编译器在编译过程中遇到inline时,将为该函数建立一段代码,然后在每次调用时直接将该代码嵌入到调用函数中,从而函数调用方式变为顺序执行方式,这一过程称为内联函数的扩展。注意:①内联函数必须在被调用之前

或定义。②内联函数中,不能含有复杂的结构控制语句,如switch、while及递归函数等。③内联函数只适合于1~5行的小函数,对于含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,所以也没有必要用内联函数实现。4.6

函数重载在C++中,如果需要定义几个功能相似而参数类型不同的函数,那么这几个函数可以使用相同的函数名,这就是函数重载。例:返回一个数的绝对值。#include<iostream>using

namespace

std;int

ABS(int);double

ABS(double);float

ABS(float);int

main(){int

x1=-12;double

x2=-12.0;float

x3=-12.0;cout<<ABS(x1)<<endl;cout<<ABS(x2)<<endl;cout<<ABS(x3)<<endl;}int

ABS(int

a){return

(a>0?a:-a);}double

ABS(double

a){return

(a>0?a:-a);}float

ABS(float

a){return

(a>0?a:-a);}只要参数个数不同,参数类型不同,参数顺序不同,函数就可以重载。然而,只是返回类型不同则不允许重载。void

func(int

a);

√void

func(char

a);

√void

func(char

a,

int

b);

√void

func(int

a,

char

b);

√char

func(int

a);

×C++按下列三个步骤的先后顺序找到匹配的函数并调用之。①寻找一个严格匹配,如果找到了,就调用那个函数。②通过相容类型的隐式转换寻求一个匹配。③通过用户定义的转换寻求一个匹配,若能查出唯一的一组转换,就用那个函数。#include<iostream>using

namespace

std;void

print(int);void

print(double);int

main(){print(1);print(1.0);print('a');print(3.14f);}void

print(int

a){cout<<"int:

"<<a<<endl;}void

print(double

a){cout<<"double:

"<<a<<endl;}输出:int:

1double:

1int:

97double:3.14函数重载例:编程求圆、矩形、梯形的面积。#include<iostream>using

namespace

std;double

area(doubler);double

area(double

h,double

w);double

area(double

a,double

b,double

h);int

main(){double

r=23.3;double

h1=12,w=5;double

a=7,b=8,

h2=3;cout<<area(r)<<endl;cout<<area(h1,w)<<endl;cout<<area(a,b,h2);}double

area(doubler){return

3.14*r*r;}double

area(double

h,double

w){return

h*w;}double

area(double

a,double

b,double

h){return

(a+b)*h/2;}4.7

函数模板C++提供了函数模板(function

template)。所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个

虚拟的类型来代表。这个通用函数就称为函

数模板。凡是函数体相同的函数都可以用这

个模板来代替,不必定义多个函数,只需在

模板中定义一次即可。在调用函数时系统会

根据实参的类型来取代模板中的虚拟类型,

从而实现了不同函数的功能。//模板

,其中T为类型参数//定义一个通用函数,用T作虚拟的类型名#include

<iostream>using

namespacestd;template<typenameT>T

max(T

a,T

b,T

c){if(b>a)a=b;if(c>a)a=c;return

a;}i=max(i1,i2,i3);d=max(d1,d2,d3);g=max(g1,g2,g3);//调用模板函数,此时T被int取代//调用模板函数,此时T被double取代//调用模板函数,此时T被long取代cout<<"i_max="<<i<<endl;cout<<"f_max="<<d<<endl;cout<<"g_max="<<g<<endl;return

0;int

main(

){int

i1=185,i2=-76,i3=567,i;double

d1=56.87,d2=90.23,d3=-3214.78,d;long

g1=67854,g2=-912456,g3=673456,g;}定义函数模板的一般形式为template

<typename

T>或template

<class

T>类型参数可以不只一个,可以根据需要确定个数。如:template<typename

T1,typenameT2>可以看到,用函数模板比函数重载更方便,程序更简洁。但应注意它只适用于函数的参数个数相同而类型不同,且函数体相同的情况,如果参数的个数不同,则不能用函数模板。4.8

有默认参数的函数C++可以给函数

中的参数使用默认值,这样在函数调用时,对应的实参就可以省略。#include<iostream>using

namespace

std;int

func(int

a=2);int

main(){cout<<func(5)<<endl;cout<<func()<<endl;}int

func(int

a){return

a;}默认参数规则:①一般来说,默认参数总是在函数

时描述。②函数参数默认值只能从后往前设置,而实参只能从前往后给出。例:void

func(inta=1,int

b,

intc=3);

×void

func(int

a,int

b=2,

intc=3);

√func(10,15,20);func(

);func(12,13);func(2,

,

20);√×√×一个函数不能既作为重载函数,又作为有默认参数的函数。(P105)?#include

<iostream>using

namespace

std;void

p(int

a,int

b=4);void

p(int

a,char

b);//voidp(inta,char

b='f');?int

main(

){p(2);p(3,'s');return0;}void

p(int

a,int

b){cout<<a<<'\t'<<b<<endl;}void

p(int

a,char

b){cout<<a<<'\t'<<b<<endl;}函数调用机制程序的内存区域一个程序要运行,就要先将可执行程序文件装载到内存中,此过程由操作系统管理,操作系统将为程序分配一个空间。程序将操作系统分配给其的运行的内存块分为四个区域:①代码区,存放程序的代码,即程序中的各个函数代码块。②全局数据区(静态区),存放程

序的全局数据和静态数据。静态区

的内容在整个程序的生命周期内都

存在,由编译器在编译的时候分配。③堆区(动态内存分配区),存放程序的动态数据。由malloc系列函数或new操作符分配的内存,其生命周期由free或delete决定。在没有

之前一直存在,直到程序结束。程序内存空间代码区全局数据区堆区栈区④栈区,存放程序的局部数据,即各个函数中的数据。栈中的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁。4.9.2

栈机制与函数调用栈是一种数据结构,栈中存取数据的原则是后进先出。C++的函数调用过程,需要初始化和善后处理环节。函数调用的整个过程就是栈空间的操作过程。函数调用时,C++做以下工作:

①建立被调用函数的栈空间,栈空间的大小由函数定义体中的数据量多少决定。②保护调用函数的运行状态和返回地址。③传递参数。④将控制权转交给被调函数。⑤函数运行完成后,

返回值到函数数据块的底部。⑥恢复调用函数的运行状态。⑦返回到调用函数。int

funcA(int

x,

int

y);int

main(

){int

a=6,b=12;a=funcA(a,

b);}int

funcA(int

x,

int

y){int

n=5;n=x+y;return

n;}main(

)b12a6操作系统运行状态及返回地址返回值0运行程序,执行main()funcA(

)n5y12x6main()运行状态及返回地址返回值main(

)b12a6操作系统运行状态及返回地址返回值0调用函数funcA(a,

b)main函数的临时空间funcA(

)n5 →

18y12x6main()运行状态及返回地址返回值main(

)b12a6操作系统运行状态及返回地址返回值0调用函数funcA(a,

b),执行n=x+y;funcA(

)n18y12x6main()运行状态及返回地址返回值18main(

)b12a6操作系统运行状态及返回地址返回值0调用函数funcA(a,

b),执行return

n;18126main()运行状态及返回地址18main(

)b12a6 →

18操作系统运行状态及返回地址返回值0返回到main()函数中a=funcA(a,b);4.10

函数的递归调用在函数体中出现调用自身的函数称为递归函数。例:阶乘n!的数学函数描述为其对应的C++函数描述为:unsigned

f(unsigned

n){if(n==1)return

1;return

n*f(n-1);}注意:n的取值范围1≤n≤12

1f

n

nf n

1n

1n

1例:Fibonacci数列的数学描述为unsigned

int

f(unsigned

int

n){if(n==1||n==2)return

1;return

f(n-1)+f(n-2);}n的取值范围1≤n≤461f

n

1n

1n

2

f

n

1

f

n

2

n

3其等价的C++函数为:递归函数在运行中,其调用与被调函数的指令代码是同一个函数副本,只不过各个不同运行中的调用点,作为状态的一部分,在栈中被分别保护起来。因此,是C++的函数机制决定了递归操作的可能性与形式。例:n!的函数,当调用f(3)时,其运行栈描述f(1)n=1f(2)函数的状态及返回地址f(2)返回值1n=2f(3)函数的状态及返回地址f(3)返回值2*f(1)n=3调用函数的状态及返回地址返回值3*f(2)递归条件:①递归不能

地调用下去,因为栈空间是有限的,所以递归函数是有条件地调用自身。例如阶乘函数中的“if(n==1)return1;”当n为1时,函数就不再递归了。②是递归函数就有递归调用语句,且递归调用应有参数,参数值应该是逐渐近停止条件。例如f(n-1)相对f(n)来说,是逐渐近了停止条件。③递归条件应先测试,后递归调用。无条件递归的逻辑错误,编译器是检查不出来的。4.11

局部变量和全局变量4.11.1

局部变量在一个函数

定义的变量是变量,它只在本函数范围内有效。同样,在复合语句中定义的变量只在本复合语句范围内有效。这称为局部变量(local

variable)。在函数开始运行时,局部数据在栈区分配空间,函数退出时,局部数据随之!!!如果局部变量不被显式初始化,其内容是不可预料的。#include

<iostream>using

namespace

std;double

f1(int

a);

//a只在这一行有效char

f2(int);

//函数

里的形参可省略int

main(){int

m=34,n=67;double

p;{char

p;p=f2(n);cout<<p<<endl;}p=f1(m);cout<<p<<endl;}double

f1(int

a){double

b=a;return

b;}char

f2(int

a){char

c=a;return

c;}p有效p有效m,

n有效b有效a有效c有效a有效4.11.2

全局变量在函数内定义的变量是局部变量,而在函数之外定义的变量是外部变量,称为全局变量(globalvariable,也称全程变量)。全局变量的有效范围为从定义变量的位置开始到本源文件结束。全局数据使得若干个模块在程序范围内能共享(读与写)数据,也是若干程序文件沟通数据的一种形式。全局数据存放在内存的全局数据区,全局数据区的整个区域在程序启动时初始化为0。注意:模块的独立性由数据的封闭性来支持。全局

数据破坏了数据的封闭性,因而对小程序而

言简单可行,对规范化程序则不登大雅之堂。函数之间用参数传递数据,尽量避免使用全局数据。4.12

变量的

类别在C++中变量除了有数据类型的属性之外,还有

类别(storage

class)

的属性。类别指的是数据在内存中方法分为静态

和动态的方法。两大类。具体包含4种:自动的(auto)、静态的(static)、寄存器的(register)和外部的(extern)。4.12.1

自动变量程序内存空间代码区全局数据区堆区栈区函数中的局部变量,如果不用关键字static加以

,编译系统对它们是动态地分配空间的。自动变量用关键字auto作存储类别的

(可以省略)。4.12.2

用static静态局部变量程序内存空间代码区全局数据区堆区栈区有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的单元不

,在下一次该函数调用时,该变量保留上一次函数调用结束时的值。这时就应该指定该局部变量为静态局部变量(static

localvariable)。对静态局部变量的说明:区内分配(1)静态局部变量在静态程序整个运行期间都不,

在动态

区区空间),函数调用结束后即局部变量)属于动态空间(而不是静态。(2)为静态局部变量赋初值是在编译时进行的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。而为自动变量赋初值,不是在编译时进行的,而是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。(3)如果在定义局部变量时不赋初值的话,对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符型变量)。而对自动变量来说,如果不赋初值,则它的值是一个不确定的值。这是由于每次函数调用结单元已

,下次调用时又重新另单元,而所分配的单元中的值是不束后分配确定的。(4)虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能

它的,也就是说,在其他函数中它是“不可见”的。例:void

func(){static

int

a=2;a++;int

b=5;b++;cout<<"a="<<a<<",

b="<<b<<endl;}int

main(){func();func();}//

a=3,

b=6//

a=4,

b=6例输出1~5的阶乘值(即1!,2!,3!,4!,5!)。//函数for(i=1;i<=5;i++)cout<<i<<"!="<<fac(i)<<endl;return

0;#include

<iostream>using

namespacestd;intfac(int);int

main(

){inti;}//f为静态局部变量,函数结束时f的值不//在f原值基础上乘以nf=f*n;return

f;intfac(intn){static

int

f=1;}*4.12.3

用register

寄存器变量一般情况下,变量的值是存放在内存中的。当程序中用到哪一个变量的值时,由控制器发出指令将内存中该变量的值送到CPU中的运算器。经过运算器进行运算,如果需要存数,再从运算器将数据送到内存存放。为提高执行效率,C++允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。这种变量叫做寄存器变量,用关键字register作

。在程序中定义寄存器变量对编译系统只是建议性(而不是强制性)的。的优化编译系统能够识别使用频繁的变量,自动地将这些变量放在寄存器中。4.12.4

用extern

外部变量全局变量(外部变量)是在函数的外部定义的,它的作用域为从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为本文件中各个函数所

。编译时将全区。全局变量,以扩展局变量分配在静态有时需要用extern来全局变量的作用域。1.

在一个文件内

全局变量如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件终了。如果在定义点之前的函数想该全局变量,则应该在

外部变量之前用关键字extern对该变量作,表示该变量是一个将在下面定义的全局变量。有了此,就可以从声明处起,合法地称为提前该全局变量,这种。,以扩例用extern对外部变量作提前展程序文件中的作用域。//函数//对全局变量a,b作提前cout<<max(a,b)<<endl;#include

<iostream>using

namespace

std;int

max(int,int);int

main(

){extern

int

a,b;}int

a=15,b=-7;//定义全局变量a,bint

max(int

x,int

y){int

z;z=x>y?x:y;return

z;}2.在多文件的程序中外部变量如果一个程序包含两个文件,在两个文件中都要用到同一个

外部变量num,不能分别在两个文件中各自定义一个外部变

量num。正确的做法是:在任一个文件中定义外部变量num,。即而在另一文件中用extern对num作外部变量extern

int

num;编译系统由此知道num是一个已在别处定义的外部变量,它先在本文件中找有无外部变量num,如果有,则将其作用域扩展到本行开始(如上节所述),如果本文件中无此外部变量,则在程序连接时从其他文件中找有无外部变量num,如果有,则把在另一文件中定义的外部变量num的作用域扩展到本文件,在本文件中可以合法地

该外部变量num。file2.cppinta=3,b=4;┆例:file1.cppextern

int

a,b;int

main(

){cout<<a<<","<<b<<endl;return0;}用extern扩展全局变量的作用域,虽然能为程序设计带来方便,但应十分慎重,因为在执行一个文件中的函数时,可能会改变了该全局变量的值,从而会影响到另一文件中的函数执行结果。4.12.5

用static

静态外部变量有时在程序设计中希望某些外部变量只限于被本文件

,而不能被其他文件外部变量时加一个staticfile1.cppstatic

int

a=3;int

main

(

){…}。这时可以在定义。例如:file2.cppextern

int

a;int

fun(intn){a=a*n;…}这种加上static

、只能用于本文件的外部变量(全局变量)称为静态外部变量。这就为程序的模块化、通用性提供了方便。如果已知其他文件不需要

本文件的全局变量,可以对本文件中的全局变量都加上static,成为静态外部变量,以免被其他文件误用。需要用静态,不要误认为用static方式(存放在静态的外部变量才采区中),而不加static的是动态

(存放在动态区)。实际上,两种形式的外部变量都用静态

方式,只是作用范围不同而已,都是在编译时分配内存的。4.13

关于变量的和定义一个函数一般由两部分组成:

(1)

部分;

(2)执行语句。

部分的作用是对有关的标识符(如变量、函数、结构体、共用体等)的属性进行说明。对于函数,

和定义的区别是明显的,在本章4.4.3节中已说明,函数的

是函数的原型,而函数的定义是函数功能的确立。对函数的

是可以放在

部分中的,而函数的定义显然不在函数的部分范围内,它是一个文件中的独立模块。对变量而言,与定义的关系稍微复杂一些。在部分出现的变量有两种情况:一种是需要建立空间的(如inta;);另一种是不需要建立空间的(如externinta;)。前者称为定义性(definingdeclaration),或简称为定义(definition)。后者称为性(referenceing

declaration)。广义地说,

包括定义,但并非所有的都是定义。对“int

a;”而言,它是定义性,既可说,又可说是定义。而对“extern

int

a;”而言,是它是而不是定义。一般为了叙述方便,把建立空间的称为定义,而把不需要建立

空间的

称为是狭义的,即非定义性。显然这里指的。例如://这是不是定义。a是一个已定义的外部变量int

main(

){extern

int

a;…}int

a;

//是定义,定义a为整型外部变量外部变量定义和外部变量

的含义是不同的。外部变量的定义只能有一次,它的位置在所有函数之外,而同一文件中的外部变量的

可以有多次,它的位置可以在函数之内,也可以在函数之外。系统根据外部变量的定义分配对外部变量的初始化只能在定义时进行,而不能在行。所谓

,其作用是向编译系统发出一个信息,变量是一个在后面定义的外部变量,仅仅是为了提前单元。中进该该变量而作的

。extern只用作

,而不用于定义。4.14

函数和外部函数函数本质上是全局的,因为一个函数要被另外的函数调用,但是,也可以指定函数只能被本文件调用,而不能被其他文件调用。根据函数能否被其他源文件调用,将函数区分为函数和外部函数。4.14.1

函数如果一个函数只能被本文件中其他函数所调用,它称为函数。在定义函数时,在函数名和函数类型的前面加static。函数首部的一般格式为static

类型标识符函数名(形参表)如static

int

fun(int

a,int

b)函数又称静态(static)函数。使用函数,可以使函数只局限于所在文件。如果在不同的文件中有同名的

函数,互不干扰。通常把只能由同一文件使用的函数和外部变量放在一个文件中,在它们前面都冠以static使之局部化,其他文件不能。4.14.2

外部函数(1)在定义函数时,如果在函数首部的最左端冠以关键字extern,则表示此函数是外部函数,可供其他文件调用。如函数首部可以写为extern

int

fun

(int

a,

int

b)这样,函数fun就可以为其他文件调用。如果在定义函数时省略extern,则默认为外部函数。本书前面所用的函数都是外部函数。(2)

在需要调用此函数的文件中,用extern

所用的函数是外部函数。例输入两个整数,要求输出其中的大者。用外部函数实现。file1.cpp(文件1)#include

<iostream>using

namespace

std;extern

int

max(int,int);//

在本函数中将要调用在其他文件中定义的max函数int

main(

){int

a,b;cin>>a>>b;cout<<max(a,b)<<endl;return

0;}file2.cpp(文件2)int

max(int

x,inty){int

z;z=x>y?x:y;returnz;}*4.15

预处理命令可以在C++源程序中加入一些“预处理命

令”(preprocessor

directives),以改进程序设计环境,提高编程效率。预处理命令是C++

规定的,但是它不是C++语言本身的组成部分,不能直接对它们进行编译(因为编译程序不能识别它们)。预处理命令语句不是C++语句,不以“;”作为语句结束标志!C++提供的预处理功能主要有以下3种:(1)

宏定义(2)

文件包含(3)条件编译分别用宏定义命令、文件包含命令、条件编译命令来实现。为了与一般C++语句相区别,这些命令以符号“#”开头,而且末尾不包含分号。4.15.1

宏定义可以用#define命令将一个指定的标识符(即宏名)来代表一个字符串。定义宏的作用一般是用一个短的名字代表一个长的字符串。它的一般形式为#define标识符字符串这就是已经介绍过的定义符号常量。如#define

PI

3.1415926还可以用#define命令定义带参数的宏定义。其定义的一般形式为#define

宏名(参数表)字符串如#define

S(a,b)a*b

//定义宏S(矩形面积),a、b为宏的参数使用的形式如下:area=S(3,2)用3、2分别代替宏定义中的形式参数a和b,即用3*2代替S(3,2)。因此赋值语句展开为area=3*2;intw=s(2+3,4);

//w的值为多少?由于C++增加了内置函数(inline),比用带参数的宏定义更方便,因此在C++中基本上已不再用#define命令定义宏了,主要用于条件编译中。4.15.2“文件包含”处理1.“文件包含”的作用所谓“文件包含”处理是指一个源文件可以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。C++提供了#include命令用来实现“文件包含”的操作。如在file1.cpp中有以下#include命令:#include

"file2.cpp"它的作用见下图示意。“文件包含”命令是很有用的,它可以节省程序设计的重复劳动。#include命令的应用很广泛,绝大多数C++程序中都包括#include命令。现在,库函数的开发者把这些信息写在一个文件中,用户只需将该文件“包含”进来即可(如调用数学函数的,应包含cmath文件),这就大大简化了程序,写一行#include命令的作用相当于写几十行、几百行甚至

行的内容。这种常用在文件头部的被包含的文件称为“标题文件”或“头部文件”。头文件一般包含以下几类内容:(1)

对类型的

。(2)

函数

。(3)

内置(inline)函数的定义。(4)宏定义。用#define定义的符号常量和用const的常变量。(5)

全局

温馨提示

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

评论

0/150

提交评论