C++程序设计-大模型思维与实践课件 第07章 函数_第1页
C++程序设计-大模型思维与实践课件 第07章 函数_第2页
C++程序设计-大模型思维与实践课件 第07章 函数_第3页
C++程序设计-大模型思维与实践课件 第07章 函数_第4页
C++程序设计-大模型思维与实践课件 第07章 函数_第5页
已阅读5页,还剩91页未读 继续免费阅读

下载本文档

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

文档简介

第七章函数C++程序设计——大模型思维与实践内容导航函数函数定义函数模板函数使用内联函数变量的作用域与生存期实践探究函数参数传递方式大模型实践默认形参大模型探究函数重载本章小结37.1.1函数定义所有函数,必须“先定义,后使用”函数定义语法格式:函数类型:为函数返回值的类型。函数执行完毕后,会返回一个这种类型的值,可以为int、float、double、char、string等。如果函数不返回任何值,类型应为void。函数名:唯一标识函数的标识符,用于在程序中调用该函数。命名应遵循“见名知意”的原则,并符合标识符的命名规范。函数类型函数名(参数类型参数1,参数类型参数2,……)

{

声明语句序列

可执行语句序列

return(表达式);

}intmain(){

inta;a=5;

return0;}47.1.1函数定义函数定义语法格式:参数列表由圆括号包围,参数之间用逗号分隔“参数类型1参数1,参数类型2参数2,……”可包含零个或多个参数每个参数均需明确指定其数据类型及名称,例如“(inta,floatb)

”表示函数接受两个参数,第一个参数是int类型的变量a,第二个参数是float类型的变量b。形参是函数从调用者那里接收的输入数据。若函数不接受任何参数,则参数列表可为空或标记为void。函数类型函数名(参数类型参数1,参数类型参数2,……)

{

声明语句序列

可执行语句序列

return(表达式);

}参数表里的变量(叫形式参数,简称形参)intmain(){

inta;a=5;

return0;}57.1.1函数定义函数类型函数名(参数类型参数1,参数类型参数2,……)

{

声明语句序列

可执行语句序列

return(表达式);

}函数体的定界符函数体由花括号{}包围的函数体包含函数执行时所需的所有声明语句和可执行语句。声明语句序列:函数体内部的一部分,可以声明变量、常量等。可执行语句序列:函数体内部的一部分,包含了函数要执行的代码。return语句用于结束函数的执行,并将执行流程返回到函数的调用者如果函数类型为void可省略结尾处的return,或使用“return;”结束函数。函数类型不为void,必须包含至少一个return语句,且return语句中的表达式类型必须与函数类型相匹配,包围表达式的圆括号可以省略。intmain(){

inta;a=5;

return0;}67.1.2函数定义示例/*函数功能:用迭代法计算n!

函数入口参数:整型变量n表示阶乘的阶数函数返回值:返回n!的值*/longlongfact(intn)/*函数定义*/{inti;longlongresult=1;for(i=2;i<=n;i++){ result*=i;}returnresult;}返回值类型函数名:fact唯一标识函数返回值参数列表声明语句序列可执行语句序列【例7-1】定义函数实现求n!(n<=20)77.1.2函数定义示例定义函数根据传入的参数打印numOfLine行*的图案。【例7-2】无参数、无返回值的函数【例7-3】有参数、无返回值的函数voidprintStar(){cout<<“*\n”;cout<<“***\n”;cout<<“*****\n”;cout<<“********\n”;cout<<“**********\n”;return;}voidprintStar(intnumOfLine){inti,j;for(i=1;i<=numOfLine;++i){for(j=1;j<=numOfLine-i;++j)cout<<‘‘;for(j=1;j<=2*i-1;++j)cout<<“*”;cout<<endl;}}定义函数打印图案

****************

*********可加void:voidprintStar(void)结尾处的return;可省略

省略return;87.1.2函数定义示例传入int型a和b,返回较大值【例7-5】有参数、有返回值的函数intgetRandomNumber(){srand(time(0));

returnrand()%10+1;}intgetMax(inta,intb){intc=a;if(b>a)c=b;returnc;}【例7-4】无参数、有返回值的函数返回一个1-10之间的随机整数97.1.2函数定义示例【例7-6】以下定义无返回值和有返回值两种空函数:空函数的函数体为空空函数是一个空的或者只有最基本实现的函数,它的主要目的是为了在软件的早期阶段就能定义出程序的主要结构。//无返回值://TODO:输出[start,end]之间的所有素数voidprintPrimes(intstart,intend){}//有返回值//TODO:返回a的e次幂的精确值intintPow(inta,inte){return0;//本语句保证函数能编译通过}内容导航函数函数定义函数模板函数使用内联函数变量的作用域与生存期实践探究函数参数传递方式大模型实践默认形参大模型探究函数重载本章小结117.2.1函数调用方式函数的定义只是为程序提供了功能实现,仅仅定义函数并不会使其自动执行。只有在程序中明确调用该函数时,相关代码才会被执行。C++程序里唯一能被自动调用执行的是main函数。每当程序运行时,操作系统会自动调用main函数,程序的执行从main函数开始。除此之外,其它函数都不会被自动调用,必须在代码中显式地调用。需要如下编写main函数,printStar函数才会执行:intmain(){

printStar(5);return0;}调用方式:函数名(参数列表)voidprintStar(intnumOfLine){inti,j;for(i=1;i<=numOfLine;++i){for(j=1;j<=numOfLine-i;++j)cout<<‘‘;for(j=1;j<=2*i-1;++j)cout<<“*”;cout<<endl;}}127.2.1函数调用方式调用方式:函数名(参数列表)参数列表:实际参数(实参)使用变量和常量作为实参

getMax(a,20);使用表达式作为实参getMax(a+20,6*5);使用函数调用的结果作为实参getMax(30,getMax(2*a,15));函数头部:intgetMax(inta,intb)137.2.1函数调用方式数量:实参和形参的数量必须一致。错误:用一个参数调用intgetMax(inta,intb)函数,如getMax(10),参数数量不一致顺序:实参和形参的顺序必须一致。调用voidprintPrimes(intstart,intend),调用方式为printPrimes(1,1000)第一个实参1会传递给第一个形参start,第二个实参1000会传递给第二个形参end,按照位置一一对应传值。147.2.1函数调用方式类型:实参和形参的类型最好一致。如果类型不一致,C++会尝试进行自动类型转换例如,getMax(2.5,3.2)中,实参向形参转换:2.523.23如果类型不一致,且无法自动转换,将报编译错误。例如getMax("1","2"),两个实参无法自动转换为int类型说明:如果形参列表为空,调用函数时,实参列表也需为空,但圆括号不能省略。调用intgetRandomNumber()调用方式:intresult=getRandomNumber()函数头部:intgetMax(inta,intb)157.2.1函数调用方式voidfun(inta,doubleb,charc){cout<<a<<""<<b<<""<<c<<endl;}intmain(){inta=1;doubleb=100.5;charc='a';fun(c,a,b);return0;}程序运行结果为:971d函数fun(c,a,b)的调用,第一个实参char型变量c会被传递给第一个int型形参a

,而不会传给第三个char型形参c,尽管第三个形参charc的变量类型和变量名都与第一个实参相同。实参和形参的对应关系严格按照它们在参数列表中的顺序来确定,不依据变量名或变量类型而改变。167.2.2函数的返回值有返回值时将返回值赋值给另一个变量

int

result=getMax(10,20);输出返回值

cout<<getMax(10,20);使用返回值(根据其类型)参与运算cout<<getMax(10,20)/3;作为另一个函数调用的参数

longlongfactResult=fact(getMax(10,20));无返回值时单独的调用语句

printPrimes(1,

1000);函数头部:intgetMax(inta,intb)函数头部:voidprintPrimes(intstart,intend)20202020177.2.2函数的返回值有返回值需要注意:在C++中,函数定义最多只能返回一个值,但是一个函数内部可以有多个return语句。执行到哪一个return语句,哪一个就起作用,函数就在那里结束,并返回相应的值。intgetMax(inta,

intb){

if

(a>b)

returna;

else

returnb;

}if-else语句有两个return语句,但是因为a>b的条件使得两个return只有其中一个被执行。如果a大于b,函数将在returna;语句结束,并返回a的值。否则,函数在returnb;语句结束,并返回b的值。intmax=getMax(20,10);intmax=getMax(20,30);187.2.2函数的返回值以下函数if(n<0)return-1;没有配对else。当n<0时,执行return-1;,函数内部其它语句会被跳过,直接返回到主调函数。如果一个函数有多个return语句,但是它们之间没有任何条件判断,那么只有第一个return语句会被执行,其它的return语句都会被忽略。如:long

longfact(int

n){

if(n<0)return-1;

inti;

long

longresult=1;

for(i=2;i<=n;i++){result*=i;}

returnresult;}intfun(){

return1;

return2;}longlongf=fact(-2);longlongf=fact(2);197.2.3函数的嵌套调用main(){……a();}a

函数{b();…return;}b函数{……return;}①③④⑤⑥⑦②voidb()

{

cout<<

"这是函数b."

<<endl;

return;

}

voida()

{

cout<<

"进入函数a."

<<endl;

b();

cout<<

"从函数b返回,准备退出函数a."

<<endl;

return;

}

intmain()

{

cout<<

"开始执行main函数."

<<

endl;

a();

cout<<

"从函数a返回,准备退出函数main."

<<endl;

return

0;

}int

main(){

cout

<<

"开始执行main函数."

<<

endl;a();

cout

<<

"从函数a返回,准备退出函数main."

<<

endl;return

0;

}voida()

{

cout<<

"进入函数a."

<<endl;

b();

cout<<

"从函数b返回,准备退出函数a."

<<endl;

return;

}

voidb()

{

cout<<

"这是函数b."

<<endl;

return;

}

类比:main->看视频a函数->接电话b函数->收快递函数可以嵌套调用,即在一个函数的内部再调用另一个函数207.2.3函数的嵌套调用—示例//计算阶乘longlong

fact(int

n){

int

i;

longlongresult=1;

for(i=2;i<=n;i++){

result*=i;

}

returnresult;

}intcomb(intn,intm){

returnfact(n)/(fact(m)*fact(n-m));}intmain(){

intn,m,c;

cin>>n>>m;

c=comb(n,m);

cout<<c;

return0;}从

n个不同元素中取出m(m≤n)个元素的所有组合的个数,用符号C(n,m)表示。现在要求从键盘输入n和m的值,计算并输出C(n,m)的值。计算公式为:C(n,m)=n!/(m!*(n-m)!)。嵌套调用过程如下:main函数首先执行,然后调用comb函数。执行流程进入comb函数:comb函数首先调用fact(n),计算n的阶乘。comb函数再调用fact(m),计算m的阶乘。comb函数调用fact(n-m),计算(n-m)的阶乘。使用以上三个返回值进行计算,返回结果执行流程回到main函数,将返回结果赋值给c,并输出c的值。21在C++编程中,函数调用过程中可能会发生三次类型转换(转换规则参照3.3节的“赋值时的类型转换”):

(1)实参到形参的类型转换。例如:函数头部:voidprint(intvalue);函数调用:print(3.5);函数的形参类型为int,但实参为double类型,实参3.5将被转换为int类型的3再赋值给形参value。(2)返回值到函数类型的转换。例如:函数定义:intgetValue(){return3.5;}函数类型为int,但return语句返回double类型的3.5,该值将被转换为int类型的3再返回,调用函数时得到的结果为3。(3)函数调用结果到赋值变量类型的转换。例如:函数类型为double,而函数的调用结果被赋值给一个int类型的变量,该变量将得到调用结果的整数部分。7.2.4函数调用的类型转换22在C++编程中,函数调用过程中可能会发生三次类型转换:(1)实参到形参的类型转换(2)返回值到函数类型的转换(3)函数调用结果到赋值变量类型的转换7.2.4函数调用的类型转换#include

<iostream>using

namespace

std;int

fun(int

a){

return

a

+

2.5;

}int

main(){

double

result

=

fun(3.5);

cout

<<

result;

return

0;}fun函数的形参类型为int,但传入了一个double类型的实参3.5,因此这个实参将被转换为int类型的3返回表达式类型为double,会被转换为int作为返回结果函数调用结果类型为int,会被转换为double237.2.5函数原型1.函数原型的作用及格式如果用户函数的定义在主调函数之后,则需要在主调函数之前声明这个自定义函数的原型。函数原型是函数的声明,它告诉编译器函数的名称、返回值类型以及参数类型。函数原型的撰写格式是函数的头部(包括返回类型、函数名和参数列表)后跟一个分号。在函数原型中,参数列表中的变量名(即参数标识符)可以省略:intadd(inta,intb);

intadd(int,int);intadd(inta,intb);//或intadd(int,int);intmain(){intx=10;inty=20;intsum=add(x,y);cout<<sum<<endl;return0;}intadd(inta,intb){returna+b;}247.2.5函数原型2.函数原型的声明位置函数原型可以在程序中的多个位置声明,以上代码在主调函数的前面声明,为最常见的情况,特别是在大型项目中,通常会在所有函数实现之前,集中声明所有的函数原型。函数原型也可在主调函数内部声明,则该声明只在此函数内部有效。intmain(){

intadd(int,int);

voidfun();intsum=add(3,5);

fun();

cout<<sum<<endl;return0;}voidfun(){cout<<add(3,5);//编译出错}intadd(inta,intb){returna+b;}257.2.5函数原型3.函数定义和函数原型的区别函数定义和函数原型的主要区别在于函数定义包含了函数体,而函数原型只是对函数的声明。函数定义是指定函数的功能,包括函数头部和函数体。函数体是函数的实现部分,包含了实现函数功能的代码。函数原型是对函数名、返回值类型、参数类型的说明,仅包括函数头部,不包括函数体。函数原型只起一个声明作用。函数原型告诉编译器函数的存在,以及函数的接口(即函数的名称、返回值类型和参数类型)。函数原型可以出现多次,但是函数定义不能重复出现,例如:voidprintPrimes(intstart,intend);voidprintPrimes(intstart,intend);//编译正确voidprintPrimes(intstart,intend){}voidprintPrimes(intstart,intend)//编译错误:函数不能重复定义{}内容导航函数函数定义函数模板函数使用内联函数变量的作用域与生存期实践探究函数参数传递方式大模型实践默认形参大模型探究函数重载本章小结277.3变量的作用域变量的作用域指源程序中所定义变量在程序中可以被访问和使用的区域。每个变量只能在其所属的作用域内被访问,超出该范围则不可见。可能在函数内部声明一个变量,也可能在所有函数之外声明一个变量。如果在一个函数内部声明一个变量,这个变量只能在这个函数内部被访问,称为局部变量。如果在所有函数之外声明一个变量,那么这个变量可以在程序的任何地方被访问,称为全局变量。287.3.1局部变量C++编程经常需要在函数或复合语句(例如if语句、switch语句、for语句等)内部定义变量。这些变量(包括函数的形参)被称为"局部变量"。局部变量的特点是只在定义它们的函数体或复合语句的范围内有效,即只能在这个范围内访问这些变量。一旦离开了这个范围,这些变量不能被访问。该变量所属的函数体或复合语句代码块,即为该变量的作用域。intmain(

)

{

intm,n;

m=

50;n=

60;

f1(m);

f2(m,n);

//cout<<a<<b;//错误

//cout<<x<<i;

//错误return

0;

}297.3.1局部变量(1)在一个函数内部定义的变量只在本函数范围内有效voidf1(

inta)

{

intb,c;

a=

10;b=

20;

c=a+b;

//m=10;

//错误}

voidf2(intx,inty)

{

inti,j;

x=

30;y=

40;i=x*y;

j=x/y;

//cout<<a<<m;

//错误doubleb;

b=5;

}a、b、c仅在此函数内有效x、y、i、j仅在此函数内有效m、n仅在此函数内有效b仅在此范围内有效两个同名变量b,分别定义在f1和f2函数中,在各自的范围内有效,互不干扰。一个函数内部定义的变量,仅可在本函数内部访问,即使两个函数定义的局部变量同名也互不干扰。307.3.1局部变量(2)在复合语句内定义的变量只在本复合语句范围内有效intmain()

{

inta,b;

a=

10;b=

20;

if

(a<b){

intc;

c=a+b;

}

//cout<<c;

//错误

for

(inti=

0;i<

10;i++){

intsum=

0;

sum+=i;

}

//cout<<i<<

""

<<sum;

//错误

{

intd;

d=

20;

}

//cout<<d;

//错误

return

0;

}a、b仅在此复合语句(函数体)内有效c仅在此复合语句内有效i、sum仅在此复合语句内有效d仅在此复合语句内有效317.3.1局部变量局部变量的特性:生存期:变量的生存期指的是变量从被创建(分配内存)到被销毁(释放内存)的时间段。局部变量的生存期遵循"定义即创建,作用域结束即销毁"的原则,具体规则如下:自动存储期:默认情况下,局部变量具有自动存储期,其生命周期由系统决定,严格受限于定义所在的代码块。创建时机:当程序执行流进入变量定义所在的代码块时(即遇到变量定义语句),编译器即为其分配内存并初始化。销毁时机:当程序执行流离开该代码块时(通过return、break、continue或闭合花括号}),变量占用的内存被自动释放,其值不再有效。327.3.1局部变量局部变量的特性:以下函数:在main函数执行f1(m);函数调用时,voidf1(inta)函数开始执行,形参变量a被创建并分配内存,并将实参m的值传递给a。程序开始执行f1的函数体,遇到变量定义语句intb,c;,此时b和c被创建并分配内存。当f1函数执行完毕,程序执行到函数体结尾的花括号时,形参a以及局部变量b和c均被销毁,内存被操作系统回收,3个变量的生存期结束。voidf1(

inta)

{

intb,c;

a=

10;b=

20;

c=a+b;

}

intmain(

)

{

intm,n;

m=

50;n=

60;

f1(m);

return

0;

}337.3.1局部变量局部变量的特性:以下复合语句:程序执行到变量d的定义语句时,d被创建,开启生存期。随后执行d=20赋值语句,紧接着遇到了复合语句的结尾花括号,d被销毁,生存期结束。说明:只要使用花括号包围即可定义复合语句块,不是只有在分支或循环语句里才能使用花括号定义复合语句{//开始一个复合语句intd;d=20;}for(inti=0;i<10;i++){

intsum=0;sum+=i;}循环开始时,i被定义(创建),并被初始化为0每次迭代的循环体被执行时,sum被定义(创建),并被初始化为0随后,sum的值增加i。一旦循环体结束(遇到结尾花括号),sum的内存就被释放,sum不再有效。下一次循环迭代开始时,sum再次被定义并初始化为0。sum的生存期非常短,第i次循环开始时被创建,该次循环结束时被释放。整个循环结束时,i被释放(详见进阶内容)347.3.1局部变量局部变量的特性:同名局部变量:不同作用域范围的语句块可以定义同名的局部变量,这些同名的局部变量互不干扰。同样,函数的形参和实参可以同名,它们也互不干扰。注意:同一个作用域内不能重复定义同名变量357.3.1局部变量-同名局部变量voidf1(inta)

{

intb,c;

a=

10;b=

20;

c=a+b;

cout<<"f1函数的a:"<<a<<endl;

}

intmain()

{

inta,b;

a=5;

f1(a);

//以下定义复合语句,以此定义一个内层作用域

{

cout<<

"main函数的a:"

<<a<<endl;

inta;

a=20;

cout<<"复合语句的a:"<<a<<endl;

}

cout<<"main函数的a:"<<a<<endl;

return

0;

}这个a、b仅在main函数内有效同名的a、b类似于不同班级的同名学生运行结果为:f1函数的a:10main函数的a:5复合语句的a:20main函数的a:5这个a、b、c仅在f1函数内有效这个a仅在复合语句内有效,有效范围到结尾的花括号f1函数的a:这个a是f1函数的形参,在f1函数内部有效。当main函数中执行f1(a)调用时,main函数的局部变量a的值被复制给f1函数的形参a,但是在f1函数内部对a的修改仅影响自己的局部变量,不会影响main函数的a变量。main函数的a:是main函数的局部变量,在main函数内部有效,包括main函数内的复合语句。复合语句里的cout<<“main函数的a:”<<a,此时访问的变量a为main函数的局部变量a。复合语句的a:这个a是复合语句的局部变量,在复合语句内部有效。一旦离开这个复合语句,这个a变量就被释放,之后再访问变量a,访问到的是main函数的a。复合语句内部定义的变量a与main函数的局部变量a,作用范围有一部分重叠。在重叠的作用范围内,作用范围小的那个变量有效。花括号开始复合语句,此作用域内可以定义同名变量a。如果此处无花括号,则不允许再定义a367.3.1局部变量-变量初始化intmain()

{

inta,sum;

cout<<a<<endl;

sum+=10;

cout<<sum;

return

0;

}

a的值为随机值对于局部变量,虽然有些编译器可能会默认将其初始化为0,但不是所有编译器都如此。sum的初始值为随机值,这里得到的结果也无法预知局部变量的特性:变量初始化:局部变量在定义时不会自动初始化,除非程序员指定初值。377.3.2全局变量在函数内定义的变量是局部变量,而在函数之外定义的变量称为全局变量(又称外部变量、全程变量)全局变量可以为本文件中所有函数所共用有效范围为从定义变量的位置开始到文件的末尾intp,q;

voidf1(inta){

intb,c;

a=

10;

p++;q++;

//c1++,c2++;

//错误}

charc1='a',c2='A’;

voidf2(intx,

inty){

inti,j;

p++;q++;

c1++;c2++;

}387.3.2全局变量全局变量可以为本文件中所有函数所共用有效范围为从定义变量的位置开始到文件的末尾intmain()

{

inta,b=10;

a=

5;

cout<<"1:p="<<p<<",q="<<q<<",c1="<<c1<<",c2="<<c2<<endl;

f1(a);

cout<<"2:p="<<p<<",q="<<q<<",c1=“<<c1<<",c2="<<c2<<endl;

f2(a,b);

cout<<"3:p="<<p<<",q="<<q<<",c1=“<<c1<<",c2="<<c2<<endl;

p++;q++;

c1++;c2++;

cout<<"4:p="<<p<<",q="<<q<<",c1=“<<c1<<",c2="<<c2<<endl;

return

0;

}程序运行结果为:1:p=0,q=0,c1=a,c2=A2:p=1,q=1,c1=a,c2=A3:p=2,q=2,c1=b,c2=B4:p=3,q=3,c1=c,c2=Cp、q的有效范围c1,c2的有效范围p、q、c1和c2都被定义为全局变量。在f1、f2和main函数中,我们都可以对p和q进行访问和操作,因为全局变量的作用范围从它们被定义的位置开始,一直延伸到文件的末尾。由于c1和c2是在f1函数之后才被定义的,所以在f1函数中无法访问到c1和c2。这是由于全局变量的作用范围规则决定的,即全局变量的作用范围从它们被定义的位置开始,直到文件的末尾。397.3.2全局变量特点:生存期:全局变量的生存期从程序启动时开始,直到程序结束时为止。这意味着全局变量在程序的整个生命周期内都存在。同名变量的屏蔽:如果在两个有一部分重叠的不同作用域中定义了同名的变量,那么作用范围小的变量会屏蔽作用范围大的变量。变量初始化:如果没有明确的初始化值,全局变量在定义时会自动初始化为0。inta=1;//全局变量aintmain(){

inta=5;//main函数中的a屏蔽了全局变量acout<<

"main函数中的a:"

<<a<<endl;//输出a的值为5,而不是全局变量a

{

inta=10;//代码块中的a屏蔽了main函数中的acout<<

"代码块中的a:"

<<a<<endl;//输出a的值为10,而不是main函数中的a

}

//在此处,复合语句内的a已不可访问,main的局部变量a的值为5,全局变量a的值为1

return0;}407.3.3extern与staticextern

int

a;

extern

void

func();

int

main()

{

cout

<<

"全局变量a的值是:"

<<

a

<<

endl;func();

return

0;

}

int

a

=

1;

void

func()

{

cout

<<

"这是函数func"

<<

endl;

}

声明全局变量a与函数func定义全局变量a与函数func在这里使用全局变量a和调用函数func在C++中,extern关键字主要用于扩展变量和函数的可见性,主要有以下两个方面的作用:(1)如果全局变量或函数定义在后面,可以在文件的前面使用extern声明该变量或函数,扩充作用域。extern关键字只是声明,不会创建新的变量或定义新的函数对于全局函数,extern是隐含的,

externvoidfunc()可以写成voidfunc()417.3.3extern与static在C++中,extern关键字主要用于扩展变量和函数的可见性,主要有以下两个方面的作用:(2)声明其它文件的全局变量或函数,将其作用域扩充到本文件。在file1.cpp中:#include<iostream>using

namespacestd;inta=1;//定义全局变量avoidfunc(){//定义函数funccout<<

"这是file1.cpp中的函数func,a的值为:"

<<

a<<endl;}在file2.cpp中:#include<iostream>using

namespacestd;extern

inta;//声明file1.cpp中的全局变量aextern

voidfunc();//声明file1.cpp中的函数funcintmain(){cout<<

"全局变量a的值是:"

<<

a<<endl;//可以在这里使用全局变量aa=2;//可以在这里修改全局变量acout<<

"全局变量a的值是:"

<<

a<<endl;//可以在这里使用全局变量a

func();//可以在这里调用函数func

return0;}声明file1.cpp中的全局变量a声明file1.cpp中的函数func运行结果:全局变量a的值是:1全局变量a的值是:2这是file1.cpp中的函数func,a的值为:2427.3.3extern与static在C++中,当全局变量或全局函数被声明为static时,它们的可访问性将被限制在声明它们的文件中,其它文件不能通过extern关键字来引用它们,防止全局变量和全局函数被其他文件误用。在file1.cpp中:#include<iostream>using

namespacestd;static

inta=1;//定义静态全局变量astatic

voidfunc(){//定义静态全局函数funccout<<

"这是file1.cpp中的函数func,a的值为:"

<<

a<<endl;}在file2.cpp中:#include<iostream>using

namespacestd;extern

inta;//尝试声明file1.cpp中的全局变量aextern

voidfunc();//尝试声明file1.cpp中的函数funcintmain(){cout<<

"全局变量a的值是:"

<<

a<<endl;//尝试使用全局变量a

func();//尝试调用函数func

return0;}定义静态全局变量a定义静态全局函数func尝试声明file1.cpp中的全局变量a和函数func尝试使用全局变量a和函数func编译错误内容导航函数函数定义函数模板函数使用内联函数变量的作用域与生存期实践探究函数参数传递方式大模型实践默认形参大模型探究函数重载本章小结447.4.1传值在执行函数调用时,实际参数(简称实参)的值会被传递(复制)给函数的形式参数(简称形参),这个过程被称为传值。这种数据传输具有单向性,即数据只能从实参传递到形参,而不能从形参反向传递到实参。实参和形参是两个完全独立的实体。任何在函数内部对形参的修改,都不会影响到实参的值。intmain()

{

inta=

2;

addOne(a);

cout<<

"a="

<<a<<endl;

return

0;

}main内存区addOne内存区axvoidaddOne(intx)

{

x++;

cout<<

"x="

<<x<<endl;

}

223voidaddOne(intx)

{

x++;

cout<<

"x="

<<x<<endl;

}

intmain()

{

inta=

2;

addOne(a);

cout<<

"a="

<<a<<endl;

return

0;

}457.4.1传值分析以上代码和运行结果,可得到如下结论:(1)数据传送的方向性:在main函数中,当调用addOne(a)时,实参a的值(即2)被复制给addOne函数中的形参x。形参x与实参a为两个完全独立的变量,尽管在参数传递后它们的值相等,它们之间没有任何其它的联系。(2)形参值的改变不影响实参:在addOne函数内部,形参x的值被增加1,但该改变不会影响实参a。(3)调用结束后的状态:从addOne返回到main函数后,形参x的内存单元被释放,而实参a的内存单元仍然保留,并且其值2不变。467.4.2传指针指针与间接访问指针是一种用于存储“地址”的特殊变量。其存在的核心价值在于提供间接访问的能力,即从一个变量访问到另一个变量,使变量访问更加灵活。假设你想拜访校长,但不清楚校长的办公室具体位置,于是你通过校长秘书获取了校长的办公室地址,随后依据这个地址顺利找到了校长。如果已知校长的办公室地址并直接前往拜访,就如同在编程中直接访问一个变量的值;通过校长秘书获取地址这一过程,则对应了指针的间接访问机制。在编程语境下,“通过某个‘中介’(如指针)获取目标对象的地址,进而实现访问”的逻辑,正是指针的核心思想。这里的“中介”可以类比为指针变量,指针变量(校长秘书)存储了目标对象(校长)的地址(即办公室地址)。在动态内存管理、复杂数据结构(如链表、树)的实现,以及函数参数传递等场景中,指针的间接访问特性都发挥了至关重要的作用。477.4.2传指针指针变量的定义指针变量存储的是另一个变量的内存地址,它的主要用途是间接访问所指向的变量。定义一个指针变量需要说明3个问题:该变量的名称是什么?该变量的类型是什么?根据该变量所存储地址找到的那块内存(即该变量所指向的那个变量),存储的是什么类型的数据(即所指向变量的数据类型)?487.4.2传指针指针变量的的定义语法格式如下:基类型*指针变量名;其中:指针变量名:指针变量的名称。基类型:该变量所指向的那个变量的数据类型。*:表示后面定义的是一个指针变量,“基类型*”为指针变量的类型名称。例如:int

*p1;

double

*p2;指针类型的解释:定义指针时,表示变量为指针的星号在语法上属于变量名,不属于前面的类型名。如果使用一个语句定义两个指针变量,需要在每个变量名前都加上星号。例如:int

*p1,

*p2;//p1和p2都是int*指针类型变量int

*p3,a;//p3是int*类型指针变量,而a是int类型变量497.4.2传指针指针变量的赋值指针变量中保存的是另一个变量的地址,从而实现间接访问。程序中所有变量在创建时都由系统自动分配内存,程序员无法知道它的内存地址。C++提供一个取地址运算符&,例如&a将返回变量a在内存中的地址。将a的地址存到变量p中注意,不能直接写p=1000;将a的地址存入变量p中:(1)变量a的地址将在程序运行时由系统分配,我们无法事先知道地址为多少;(2)p为int*类型,而1000为整型,两个类型不相同,不可执行赋值操作。变量a在内存中的地址为1000(因为int型的变量将占据4字节,实际上将占用1000-1003共4个字节,但我们通常用其首地址1000作为该变量的内存地址)变量p在内存中也有一个地址,我们这里不关心该地址值。执行p=&a后,p中存储的内容为1000,即变量a的地址。例如:inta=

10;

int

*p;

p=

&a;p&aa10变量p存储的是“&a”,但我们不关心a的具体地址值为多少,画图可以简化:对于指针变量,除了可以将一个变量的地址赋给它之外,还可以将另一个同类型的指针变量的值赋给它。一个指针明确设定为不指向任何变量,可将其赋值为NULL(或nullptr),称为空指针。NULL是C++定义的一个符号常量,它的值为整数0。507.4.2传指针指针变量定义的时候也可以进行初始化。形式如下:注意:(1)指针所指向的变量,其类型必须与该指针的基类型相同。

例如:有定义int*p;floatf;,赋值p=&f;错误。(2)两个指针之间赋值,它们的类型必须相同。

例如:有定义int*p1;float*p2;,赋值p1=p2;错误。inta=10;

int

*p=&a;

inta=10;

int

*p1,

*p2;

p1=&a;

p2=p1;517.4.2传指针指针变量的访问C++定义了一个间接访问运算符“*”(称为解引用运算符)。*运算符是一元运算符,它的运算对象为指针变量。“*指针变量”返回其指向的变量。例如:inta=

10;

int

*p=

&a;*p=

20;inty=*p;将*p看作另一个变量的别名,前提是p指向了某一个变量,否则将导致错误。例如:int*p;//p中的内容为随机值,不指向任何变量*p=20;//错误,p未指向任何变量时,不能使用*pint*q=NULL;//q初始化为NULL,即明确指出其不指向任何变量*q=20;//错误,q的值为NULL时,不能使用*qp&aa10别名*p20y20527.4.2传指针指针变量的访问注意区分两个“*”的含义:(1)int*p=&a;,“*”的含义为:定义的变量p为指针类型变量。该语句可以拆分成两个语句:int*p;p=&a;,注意,不要错误地写成:int*p;*p=&a;(2)*p=20;,“*”的含义为解引用操作,*p为变量a的别名。537.4.2传指针指针变量的访问注意区分对指针变量p的赋值和对*p的赋值。例如有定义:inta=10,b=20;int*p1=&a,*p2=&b;区分p2=p1;和*p2=*p1;操作的结果:使用*p间接访问一个变量,增加了麻烦,特别是对变量a,为什么不用变量名a访问该变量,而需要用*p去访问它?voidaddOne(int

*x)

{

(*x)++;

cout<<

"*x="

<<

*x<<endl;

}

intmain()

{

inta=

2;addOne(&a);

cout<<"a="<<a<<endl;return0;}547.4.2传指针向函数传递指针如果形参为指向实参的指针,可以通过形参间接修改实参的值。intmain()

{

inta=

2;addOne(&a);cout<<"a="<<a<<endl;return0;}voidaddOne(int

*x)

{

(*x)++;

cout<<

"*x="

<<

*x<<endl;

}

addOne内存区main内存区xa23别名*x&a557.4.2传指针以上代码中,包括三个步骤:1.

指针传递:main函数调用addOne函数时,将变量a的地址&a传递给了addOne函数的形参x。x存储了a的内存地址,相当于给a取了一个别名*x,对*x的操作即为对main函数中的a进行操作。2.解引用操作:执行(*x)++操作等同于执行a++。注意:由于后置++的右结合性,对*x加1的操作需要写成(*x)++,而不能写成*x++。如果换一种写法:*x=*x+1,此时*x不需要用圆括号包围。*x++的含义是:先取*x的值,然后执行x++操作。3.调用结束:addOne函数执行完成后,执行流程返回到main函数,形参x的内存被释放,而实参a的值已被改变。567.4.2传指针—指针其它用途使用指针从函数返回多个值例如://函数使用指针返回两个整数的和与差

voidcalculateSumAndDifference(inta,

intb,

int*pSum,

int*pDifference)

{

*pSum=a+b;

*pDifference=a-b;

}

intmain()

{

intnum1=

10,num2=

5;

intsum,difference;

calculateSumAndDifference(num1,num2,

&sum,

&difference);

cout<<

"和:"

<<

sum<<

",差:"

<<

difference<<endl;

return

0;

}通过解引用指针来修改这些指针所指向的值,从而实现了返回多个值的效果注意:在使用解引用*pSum、*p时,一定要明确pSum和p指针指向哪块内存。使用以下方式调用函数,将引发未定义行为,甚至使得程序崩溃:

intnum1=10,num2=5;

int*p,*q;

calculateSumAndDifference(num1,num2,p,q);

cout<<"和:"<<*p<<",差:"<<*q<<endl;

577.4.3传引用C++对指针进行了封装,提供“引用”的语法,从而具有较高的安全性引用是C++特有的语法,是对已存在的变量取一个别名,对引用的任何操作都会直接反映到它所引用的变量上。引用在定义时必须被初始化。定义引用变量的语法格式如下:

数据类型

&引用名称

=

变量名;系统并不会为引用变量分配新的内存空间,是为它所绑定的变量取了一个别名例如,可以定义一个整型变量a和一个引用r,然后将r绑定到a上:inta=

10;

int

&r=a;

r=

5;

定义引用r,绑定到a上通过r修改a的值a10别名r5587.4.3传引用注意事项:引用是已存在变量的别名,当定义一个引用时,系统并不会为引用分配新的内存空间。定义引用时必须立即对它初始化,不能定义完成后再进行绑定。一旦引用被定义并初始化,它就会被绑定到一个变量上,不能再将它绑定到其他变量上。以下代码错误:int

&r;

inta=

10;

r=a;

inta=

10;

int

&r1=a;

int

&r2=r1;

引用的初始值可以是一个变量或另一个引用。597.4.3传引用前一节的addOne函数的形参修改为引用,如下:voidaddOne(int

&x)

{

x++;

cout<<

"x="

<<x<<endl;

}

intmain()

{

inta=

2;

addOne(a);

cout<<

"a="

<<a<<endl;

return

0;

}intmain()

{

inta=

2;

addOne(a);

cout<<

"a="

<<a<<endl;

return

0;

}voidaddOne(int

&x)

{

x++;

cout<<

"x="

<<x<<endl;

}

addOne内存区main内存区a2别名x3607.4.4应用实例:交换变量C++中函数参数的传递方式主要有三种:传值、传指针和传引用。下面以交换两个变量值的swap函数为例,对比三种传递方式的使用和区别。(1)传值:函数调用时,将实参的值复制一份给形参。voidswap(intm,

intn)

{

inttemp=m;

m=n;

n=temp;

cout<<

"m="

<<m<<

",n="

<<n<<endl;

}

intmain(){

inta=

10,b=

20;

swap(a,b);

cout<<

"a="

<<a<<

",b="

<<b<<endl;

return

0;

}intmain(){

inta=

10,b=

20;

swap(a,b);

cout<<

"a="

<<a<<

",b="

<<b<<endl;

return

0;

}voidswap(intm,

intn)

{

inttemp=m;

m=n;

n=temp;

cout<<

"m="

<<m<<

",n="

<<n<<endl;

}

main内存区swap内存区abmntemp10201020102010617.4.4应用实例:交换变量C++中函数参数的传递方式主要有三种:传值、传指针和传引用。下面以交换两个变量值的swap函数为例,对比三种传递方式的使用和区别。(2)传指针:函数调用时,传递变量的地址给函数,函数内部通过解引用(使用*操作符)访问和操作原始数据。voidswap(int*m,

int*n)

{

inttemp=

*m;

*m=

*n;

*n=temp;

cout<<"*m="<<*m<<",*n="<<*n<<endl;

}

intmain(){

inta=

10,b=

20;

swap(&a,

&b);

cout<<

"a="

<<a<<

",b="

<<b<<endl;

return

0;

}

intmain(){

inta=

10,b=

20;

swap(&a,

&b);

cout<<

"a="

<<a<<

",b="

<<b<<endl;

return

0;

}

voidswap(int*m,

int*n)

{

inttemp=

*m;

*m=

*n;

*n=temp;

cout<<"*m="<<*m<<",*n="<<*n<<endl;

}

main内存区ab1020swap内存区mntemp&a&b102010实参为实参为别名*n别名*mvoidswap(int*m,

int*n)

{

int*temp=m;

m=n;

n=temp;

cout<<"*m="<<*m<<",*n="<<*n<<endl;

}

intmain(){

inta=

10,b=

20;

swap(&a,

&b);

cout<<

"a="

<<a<<

",b="

<<b<<endl;

return

0;

}

627.4.4应用实例:交换变量C++中函数参数的传递方式主要有三种:传值、传指针和传引用。下面以交换两个变量值的swap函数为例,对比三种传递方式的使用和区别。(2)传指针:在函数内部如果只修改指针,而不是根据别名修改对应的变量,则实参不受影响。intmain(){

inta=

10,b=

20;

swap(&a,

&b);

cout<<

"a="

<<a<<

",b="

<<b<<endl;

return

0;

}

voidswap(int*m,

int*n)

{

int*temp=m;

m=n;

n=temp;

cout<<"*m="<<*m<<",*n="<<*n<<endl;

}

main内存区ab1020swap内存区mntemp&a&b&a实参为实参为别名*n别名*m&b&a别名*m别名*n637.4.4应用实例:交换变量C++中函数参数的传递方式主要有三种:传值、传指针和传引用。下面以交换两个变量值的swap函数为例,对比三种传递方式的使用和区别。(3)传引用:允许函数直接通过别名访问原始数据。与传指针类似,传引用也能实现直接修改原始数据的目的,但语法上更为简洁和安全。:voidswap(int

&m,

int

&n)

{

inttemp=m;

m=n;

n=temp;

cout<<"m="<<m<<",n="<<n<<endl;

}

intmain(){

inta=

10,b=

20;

swap(a,b);

cout<<

"a="

<<a<<

",b="

<<b<<endl;

return

0;

}intmain(){

inta=

10,b=

20;

swap(a,b);

cout<<

"a="

<<a<<

",b="

<<b<<endl;

return

0;

}voidswap(int

&m,

int

&n)

{

inttemp=m;

m

温馨提示

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

评论

0/150

提交评论