序言
一个 C++ 程序无论大小,都由一个或者多个函数组成,而且其中必须有且只有一个函数main(),称之为“主函数”。
由函数 main()调用其他函数来完成程序的特定功能。当然,其他函数之间也可以按照规则互相调用。
C++ 中的函数由一段相对独立的代码组成,这段代码能实现某一项具体、独立、完整的功能。
一、使用函数的目的:
①“代码重用”:代码重用是保证同一个函数可以被一个或多个函数调用任意多次,从而减少重复代码的编写。
②“问题分解”:问题分解可以保证一个大的程序(或者说软件),按照模块化编程思想,由大化小,分解成若干个结构清晰、功能独立、调试方便的函数,甚至给若干人合作完成,从而提高开发效率。
二、函数有两种:
一种是库函数:也称系统函数 ,如:sqrt() strcpy() memset()。
二种是自定义函数:用户自已根据需要编写的函数。
三、自定义函数的定义:
四、返回值
1、如果在定义函数时不指定函数类型,系统会隐含指定函数类型为int,函数结束也需返回一个int型值。
2、函数的返回值由 return 语句给出。 return(表达式) 或 return 表达式;
3、如果函数没有返回值,函数名前的类型标识符为void,return 语句可省略不写。
4、如果return中的值与函数值的类型不一致,则以函数类型为准。即在返回时先作隐含的类型转换,然后再返回。如果类型不相容,不能转换,则编译会出错。
#include<bits/stdc++.h>
using namespace std;int f(void)
{ return 3.5;} //可以写成return 3.5也可以写成 return (3.5);int main()
{ int a;a=f();cout<<“a=”<<a;
}
3.5 被转换成3后,返回给f()函数。 输出a=3。
#include<bits/stdc++.h>
using namespace std;
int main()
{ float a,b;int c;int max(float x,float y);scanf("%f %f",&a,&b);c=max(a,b);printf("Max is %d\n",c);
}
int max(float x, float y)
{ if(x>y) return x;else return y;
}
输入: 1.5 3.4
输出:
五、函数的四种类型
1、有参数无返回值
#include<bits/stdc++.h>
using namespace std;void fun(int n) //无返回值函数要写void类型
{ int i;for(i=1; i<=n; i++)cout<<i;cout<<endl;return; //无返回值函数可以写return;也可以不写
}
int main()
{ int x;cin>>x;fun(x);return 0;
}
输入: 5
输出:
2、有参数有返回值
#include<bits/stdc++.h>
using namespace std;long power(int x, int n)
{ long p=1;for(int i=1; i<=n; i++) p=p*x;return p; //可以写成return p;也可以写成return (p);
}int main()
{ int a,b,c;cin>>a>>b;c=power(a,b);cout<<c;
}
输入: 5 2
输出:
3、无参数无返回值
#include<bits/stdc++.h>
using namespace std;void show1(void)
{ cout<<"***";}int main()
{ show1(); return; //无返回值函数也可以用return; 但后面不接变量,也不会返回值。
}
输出 :
4、无参数有返回值
#include<bits/stdc++.h>
using namespace std;int geti(void)
{ int i; cin>>i;return i;
}int main()
{ int a; a=geti(); cout<<a;}
输出:
六、函数的声明:
函数在调用使用前要先声明,这点和变量在使用前需声明的情形非常类似。
函数的声明又称为函数的原型。函数原型就是把函数头部复制到主函数的上面,在后面加上分号“;”,参数列表可以只写数据类型,不写变量名。
函数原型的通式为:
返回数据类型 函数名 (参数数据类型列表)
函数如果定义在所有函数之前,那该函数在本程序中任何地方都有效。如果定义在主函数内,那只在主函数内有效。
#include<bits/stdc++.h>
using namespace std;int js(int n) //js()函数定义在主函数前,所以不要声明{ int z,i;z=1;for (i=1;i<=n;i++)z=z * i;return z;
}int main()
{ cout<<js(4)<<endl;
}
输出:
#include<bits/stdc++.h>
using namespace std;( ); //因为js()函数定义在主函数后面,所以要函数声明。请写出函数原型
int main()
{ cout<<js(4)<<endl;
}int js(int n)
{ int z,i;z=1;for (i=1;i<=n;i++)z=z * i;return z;
}
输出:
#include <iostream>
using namespace std;
int main()
{ int a,b;int fun1(int x,int y);//函数声明,函数fun1()在主函数中声明,所以只在主函数中能被引用cin>>a>>b;cout <<fun1(a,b)<<endl;
}int fun1(int x,int y)
{ int fun2(int m); //函数fun2()在fun1()函数中声明,所以只在fun1()函数中能被引用return (fun2(x)+fun2(y));
}int fun2(int m)
{ return (m*m);
}
输入: 5 2
输出:
七、函数的调用
在程序中以任何方式对函数的使用都称为函数调用。
1、以表达式形式调用: 函数调用的结果作为表达式的一部分。
2、以语句的形式调用: 函数调用作为一条独立语句,完成一件事情(一系列操作),没有任何返回值。例如:print (n);
3、 作为另一个函数的参数被调用。 以实参形式 出 现在其他函数调用中。(如sort()函数)
4、被其它函数嵌套调用
5、被自身嵌套调用,称为递归调用
函数调用的一般形式为: 函数名(实参1,实参2,…,实参n)
7.1、表达式调用
#include <iostream>
using namespace std;
int main()
{ int a,b;int fun1(int x,int y);//函数声明cin>>a>>b;cout <<fun1(a,b)<<endl;
}int fun1(int x,int y)
{ int fun2(int m);return (fun2(x)+fun2(y));
}int fun2(int m)
{ return (m*m);
}
输入:1 3
输出
#include<bits/stdc++.h>
using namespace std;
int fac(int n)
{ int z = 1;for(int i = 1; i <= n; i++) z = z * i;return z;
}
int main()
{ int x = fac(5) + fac(4);// 函数调用出现在表达式中cout << x << endl;return 0;
}
输出
7.2、语句调用
#include<bits/stdc++.h>
using namespace std;void show1(void)
{ cout<<"***";}int main()
{ show1(); //show1()是无返回值的函数。所以用语句的形式调用,return 0;
}
输出
#include<bits/stdc++.h>
using namespace std;
void show2(int n) //这里的 n 为形式参数
{ int i;for(i=1; i<=n; i++)cout<<"*";cout<<endl;
}
int main()
{ show2(1); //show2()是无返回值的函数。所以用语句的形式调用,show2(2);show2(3); //括号内的1、2、3、4是实际参数。show2(4);return 0;
}
输出
#include<bits/stdc++.h>
using namespace std;
void show2(int n)
{ int i;for(i=1; i<=n; i++)cout<<"*";cout<<endl;
}
int main()
{ int i,n;cout<<"输入行数";cin>>n;for(i=1;i<=n;i++)show2(i); //show2()是无返回值的函数。所以用语句的形式调用, 这里的 i 也是实际参数return 0;
}
输入 3
输出
#include<bits/stdc++.h>
using namespace std;
void maxnum(int x,int y)
{ int w = x > y ? x : y;cout << w << endl;
}
int main()
{ int a = 5,b = 22;maxnum(a,b);// 函数调用作为一条独立语句return 0;
}
输出
7.3、作为另一个函数的参数被调用
#include<bits/stdc++.h>
using namespace std;
int big(int x,int y);// 函数的提前声明
int main()
{ int x,y,z;cin >> x >> y >> z;cout << big(big(x,y),z) << endl;// 函数调用的返回值又作为其他函数调用的实际参数
}
int big(int x,int y) // 函数定义
{ if(x > y) return x;else return y;
}
输入 3 9 5
输出
例7、拼数:(1998 NOIP提高组)
设有n个正整数(n≤20),将它们联接成一排,组成一个最大的多位整数。
例如:n=3时,3个整数13,312,343联接成的最大整数为:34331213
又如:n=4时,4个整数7,13,4,246联接成的最大整数为:7424613
输入样例
3
13 312 343
输出样例
34331213
#include<bits/stdc++.h>
using namespace std;
string a[20];bool cmp(string a,string b) // cmp是自定义的函数名,名字由自已取。
{return a+b>b+a; //a+b就是b接在a的后面
}int main()
{ int n;cin>>n;for(int i=1;i<=n;i++)cin>>a[i];sort(a+1,a+n+1,cmp); //作为另一个函数的参数被调用for(int i=1;i<=n;i++)cout<<a[i];
}
为什么不直接比较a和b?
举个例子:当输入 2 300 30时,容易知道答案为30300。
可是如果单纯的字符串比较, 那么答案为30030。
因此要比较b+a和a+b
7.4、嵌套调用(函数相互之间的调用)
例 求三个数中最大数和最小数的差值,
#include <bits/stdc++.h>
int dif(int x,int y,int z);
int max(int x,int y,int z);
int min(int x,int y,int z);int main()
{ int a,b,c,d;scanf("%d%d%d",&a,&b,&c);d=dif(a,b,c);printf("Max-Min=%d\n",d);
}int dif(int x,int y,int z)
{ return max(x,y,z)-min(x,y,z);}int max(int x,int y,int z)
{ int r; r=x>y?x:y;return(r>z?r:z);}int min(int x,int y,int z)
{ int r; r=x<y?x:y;return(r<z?r:z);}
输入:2 5 9
输出:
例:数的分离
【问题描述】
定义一函数 digit (n,k) 分离出整数 n 从右边数第 k 个数字。如 digit(2076,1) 等于 6,而 digit(2076,5) 等于 0。main 函数输入 n 和 k,调用 digit(n,k) 输出答案,n 在 long long 范围内。
【输入格式】
一行两个整数分别表示 n 和 k,之间用一个空格隔开。
【输出格式】
一行一个整数,表示整数 n 从右边数第 k 个数字。
【输入样例】
31859 3
【输出样例】
8
#include<bits/stdc++.h>
using namespace std;
int digit(long long n,int k)
{ int tmp;for(int i = 1; i <= k; i++){ tmp = n % 10;n = n / 10;}return tmp;
}
int main()
{ long long n;int k;cin >> n >> k;cout << digit(n,k) << endl;return 0;
}
例、统计闰年
【问题描述】
输入两个年份 x 和 y,统计并输出公元 x 年到公元 y 年之间的所有闰年数(包括 x 年和 y 年),1≤x≤y≤3000。
【输入格式】
一行两个正整数表示 x 和 y,之间用一个空格隔开。
【输出格式】
一行一个正整数,表示公元 x 年到公元 y 年之间的所有闰年数。
【输入样例】
2000 2004
【输出样例】
2
#include<bits/stdc++.h>
using namespace std;
bool rn(int n)
{ if((n % 4 == 0) && (n % 100 != 0) || (n % 400 == 0)) return true;else return false;
}
int main()
{ int x,y,t = 0;cin >> x >> y;for(int i = x; i <= y; i++)if(rn(i)) t++;cout << t << endl;return 0;
}
动手练习:
2、计算s=1!+2!+3!+….+n! 的和,要求用自定义函数实现。
3、 编写函数,函数功能是:判断输入的字符是否为数字字符。如果是,则输出YES,否则输出NO,要求输入输出均在主函数中完成。
4、编写两个函数,函数功能分别是:求两个整数的最大公约数和最小公倍数,要求输入输出均在主函数中完成。
5、编写函数digit(num, k),函数功能是:求整数num从右边开始的第k位数字的值,如果num位数不足k位则返回0。要求输入输出均在主函数中完成。
6、编写函数,求一个字符串的长度。在主函数中调用该函数,输出结果。输入字符串(允许包含空格),输出字符串的长度。
7、输入一个字符串,将其中所有的小写字母转换成大写字母。要求用自定义函数实现。
方法一:
#include<bits/stdc++.h>
using namespace std;
int main()
{ char a[256];gets(a);strupr(a);cout<<a;
}
方法二:
#include<bits/stdc++.h>
using namespace std;
int main()
{ char a[256];int i,l;gets(a);l=strlen(a);for(i=0;i<l;i++)if(islower(a[i]))a[i]=toupper(a[i]);cout<<a;
}
八、内联函数(内置函数)
调用函数时需要一定的时间和空间的开销。下图表示函数调用的过程:
C++提供一种提高效率的方法,即在编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程转出去。这种嵌入到主调函数中的函数称为内置函数(inline function),又称内嵌函数。在有些书中把它译成内联函数。
例如: 函数指定为内置函数。
#include <iostream>
using namespace std;
inline int max(int,int, int); //声明函数,左端有inlineint main( )
{ int i=10,j=20,k=30,m; m=max(i,j,k); cout<<"max="<<m<<endl; return 0;
} inline int max(int a,int b,int c) //定义max为内置函数
{ if(b>a) a=b; if(c>a) a=c; return a;
}
指定内置函数的方法很简单,只需在函数首行左端加一个关键字inline即可。由于在定义函数时指定它为内置函数,因此编译系统在遇到函数调用max(i,j,k)时,就用max函数体的代码 ( if (j>i) i=j; if(k>i) i=k; m=i; )代替max(i,j,k),同时将实参代替形参。这样, 程序就被置换成:
#include <iostream>
using namespace std;
inline int max(int,int, int);
int main( )
{ int i=10,j=20,k=30,m; if (j>i) i=j; //语句m=max(i,j,k); 被置换if(k>i) i=k; m=i; cout<<"max="<<m<<endl; return 0;
}
inline int max(int a,int b,int c)
{ if(b>a) a=b; if(c>a) a=c; return a;
}
注意:
1、可以在声明函数和定义函数时同时写inline,也可以只在其中一处声明inline,都能按内置函数处理。
2、使用内置函数可以节省运行时间,但却增加了目标程序的长度。因此一般只将规模很小(一般为5个语句以下)而使用频繁的函数声明为内置函数。
3、内置函数中不能包括复杂的控制语句,如循环语句和switch语句。
4、对函数作inline声明,只是程序设计者对编译系统提出的一个建议,而不是指令性的。并非一经指定为inline,编译系统就必须这样做。编译系统会根据具体情况决定是否这样做。
5、归纳起来,只有那些规模较小而又被频繁调用的简单函数,才适合于声明为inline函数。
九、函数重载
1、概念
在编程时,有时我们要实现的是同一类的功能,只是有些细节不同。例如希望从3个数中找出其中的最大者,而每次求最大数时数据的类型不同,可能是3个整数、3个双精度数或3个长整数。在C语言中程序设计者往往要分别设计出3个不同名的函数,其函数原型为:
int max1(int a,int b, int c); //求3个整数中的最大者
double max2(double a,double b,double c); //求3个双精度数中最大者
long max3(long a,long b,long c); //求3个长整数中的最大者
C++允许用同一函数名定义多个函数,即相同函数名,但是在形参的个数、类型与顺序上有所不同,编译器会根据实参,与形参进行最佳匹配,自动确定应该调用哪个函数, 使一个函数名可以多用,这就是函数的重载。
2、举例
例1:运用函数重载求3个数中最大的数(分别考虑整数、双精度数、长整数的情况)。
#include <iostream>
using namespace std;
int main( )
{ int max(int a,int b,int c);double max(double a,double b,double c);long max(long a,long b,long c);int i1, i2, i3, i; cin>>i1>>i2>>i3; i=max(i1,i2,i3);cout<<"i_max="<<i<<endl;double d1, d2, d3, d; cin>>d1>>d2>>d3;d=max(d1,d2,d3);cout<<"d_max="<<d<<endl;long g1,g2,g3,g;cin>>g1>>g2>>g3;g=max(g1,g2,g3);cout<<"g_max="<<g<<endl;
}int max(int a,int b,int c) //定义求3个整数中的最大者的函数
{ if(b>a) a=b;if(c>a) a=c;return a;
}double max(double a,double b,double c)//定义求3个双精度数中的最大者的函数
{ if(b>a) a=b;if(c>a) a=c;return a;
}long max(long a,long b,long c) //定义求3个长整形数中的最大者的函数
{ if(b>a) a=b;if(c>a) a=c;return a;
}
输入:
185 -76 567 (输入3个整数)
56.87 90.23 -32.78 (输入3个实数)
67854 -9156 673456 (输入3个长整数)
输出:
i_max=567 (输出3个整数的最大值)
d_max=90.23 (输出3个双精度数的最大值)
g_max=673456 (输出3个长整数的最大值)
上例3个max函数的函数体是相同的,其实重载函数并不要求函数体相同。重载函数除了允许参数类型不同以外,还允许参数的个数不同。
例2:编写一个程序,用来求两个整数或3个整数中的最大数。如果输入两个整数,程序就输出这两个整数中的最大数,如果输入3个整数,程序就输出这3个整数中的最大数。
#include <iostream>
using namespace std;
int main( )
{ int max(int a,int b,int c); //函数声明int max(int a,int b); //函数声明int a=8,b=-12,c=27;//输出3个整数中的最大者cout<<"max(a,b,c)="<<max(a,b,c)<<endl; //输出两个整数中的最大者cout<<"max(a,b)="<<max(a,b)<<endl;
}
//此max函数的作用是求3个整数中的最大者
int max(int a,int b,int c)
{ if(b>a) a=b;if(c>a) a=c;return a;
}
//此max函数的作用是求两个整数中的最大者
int max(int a,int b)
{ if(a>b) return a;else return b;
}
输出:
max(a,b,c)=27
max(a,b)=8
两次调用max函数的参数个数不同,系统就根据参数的个数找到与之匹配的函数并调用它。
3、总结
参数的个数和类型可以都不同。但不能只有函数的类型不同而参数的个数和类型相同。
例如:
int f(int); //函数返回值为整型
long f(int); //函数返回值为长整型
void f(int); //函数无返回值
在函数调用时都是同一形式,如“f(10)”。编译系统无法判别应该调用哪一个函数。重载函数的参数个数、参数类型或参数顺序3者中必须至少有一种不同,函数返回值类型可以相同也可以不同。
在使用重载函数时,同名函数的功能应当相同或相近,不要用同一函数名去实现完全不相干的功能,虽然程序也能运行,但可读性不好,使人莫名其妙。
十、实参与形参
1、概念
形式参数:在定义函数时,函数名后括号中的变量称为形式参数(简称形参)
实际参数:在主程序中调用一个函数时,函数名后括号中的参数称为实际参数
(简称实参)
1、对于形参和实参的说明:
(1) 对于被调用函数中的形参变量,在程序未调用该函数前,并不占内存的存储空间,只有在被调用函数max执行时,形参变量才被分配临时存储空间。函数调用完毕,形参变量的存储空间将被内存收回。
(2)实参可以为常量、变量或表达式、数组元素、数组名、指针、结构体变量、枚举变量、对象、函数名等。如 show2(i) 但要求 i 有确定的值。以便在调用函数时将实参的值赋给形参。
(3)在定义函数时,必须在函数首部指定形参的类型。
(4)实参与形参的类型应相同或赋值兼容。实参和形参都是整型,这是合法的、正确的。如果实参为整型而形参为实型,或者相反,则按不同类型数值的赋值规则进行转换。例如实参a的值为3.5,而形参x为整型,则将3.5转换成整数3。字符型与整型可以互相通用。
(5)实参变量对形参变量的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回给实参。在调用函数时,编译系统临时给形参分配存储单元。
请注意:实参单元与形参单元是不同的存储单元。
2.形参和实参的结合:
函数参数传递过程就是形参和实参相结合的过程,形参和实参的结合遵守以下规则:
(1) 个数一致:形参有多少个数据项,在调用时实参就必须有多少个数据项
(2) 顺序一致:若要把实参v的值传递给形参w,在调用时v在实参中的顺序必须等于w在形参中的顺序,数据类型也要保持一致。
(3)实参表求值顺序, 自右向左。
#include<bits/stdc++.h>
int f(int a, int b);
int main()
{ int i=2,p;p=f(i,++i); printf("%d",p);
}
int f(int a, int b)
{ int c;if(a>b) c=1;else if(a==b) c=0;else c=-1;return(c);
}
输出:0
#include<bits/stdc++.h>
int f(int a, int b);
int main()
{ int i=2,p;p=f(i,i++); printf("%d",p);
}
int f(int a, int b)
{ int c;if(a>b) c=1;else if(a==b) c=0;else c=-1;return(c);
}
输出:1
3、有默认参数的函数:
一般情况下,在函数调用时形参从实参那里取得值,因此实参的个数应与形参相同。有时多次调用同一函数时用同样的实参,C++提供简单的处理办法,给形参一个默认值,这样形参就不必一定要从实参取值了。
如有一函数声明 float area(float r=6.5); 指定r的默认值为6.5,如果在调用此函数时,确认r的值为6.5,则可以不必给出实参的值, 如 area( ); //相当于area(6.5);
如果不想使形参取此默认值,则通过实参另行给出。
如 area(7.5); //形参得到的值为7.5,而不是6.5
这种方法比较灵活,可以简化编程,提高运行效率。
如果有多个形参,可以使每个形参有一个默认值,也可以只对一部分形参指定默认值,另一部分形参不指定默认值。
如有一个求圆柱体体积的函数,形参h代表圆柱体的高,r为圆柱体半径。
函数原型如下: float volume(float h,float r=12.5); //只对形参r指定默认值12.5
函数调用可以采用以下形式:
volume(45.6); //相当于volume(45.6,12.5)
volume(34.2,10.4) //h的值为34.2,r的值为10.4
实参与形参的结合是从左至右顺序进行的。因此指定默认值的参数必须放在形参表列中的最右端,否则出错。例如:
void f1(float a,int b=0,int c,char d=′a′); //不正确
void f2(float a,int c,int b=0, char d=′a′); //正确
如果调用上面的f2函数,可以采取下面的形式:
f2(3.5, 5, 3, ′x′) //形参的值全部从实参得到
f2(3.5, 5, 3) //最后一个形参的值取默认值′a′
f2(3.5, 5) //最后两个形参的值取默认值,b=0,d=′a′
可以看到,在调用有默认参数的函数时,实参的个数可以与形参的个数不同,实参未给定的,从形参的默认值得到值。利用这一特性,可以使函数的使用更加灵活。例如求2个数或3个数中的最大数,也可以不用重载函数,而改用带有默认参数的函数。
例:求2个或3个正整数中的最大数,用带有默认参数的函数实现。
#include <iostream>
using namespace std;
int main( )
{int max(int a, int b, int c=0); //函数声明,形参c有默认值
int a,b,c;cin>>a>>b>>c;cout<<“max(a,b,c)=”<<max(a,b,c)<<endl; //输出3个数中的最大者cout<<“max(a,b)=”<<max(a,b)<<endl; //输出2个数中的最大者return 0;
}
int max(int a,int b,int c) //函数定义
{if(b>a) a=b;if(c>a) a=c;return a;
}
输出:
14 -56 135
max(a,b,c)=135
max(a,b)=14
在使用带有默认参数的函数时有两点要注意:
(1)如果函数的定义在函数调用之后,则在函数调用之前需要有函数声明,此时必须在函数声明中给出默认值,在函数定义时不允许给默认值。 如果函数的定义在函数调用之前,则应在函数定义中给出默认值。
(2) 一个函数不能既作为重载函数,又作为有默认参数的函数。因为当调用函数时如果少写一个参数,系统无法判定是利用重载函数还是利用默认参数的函数,出现二义性,系统无法执行。
十一、函数调用时参数的传递
#include <bits/stdc++.h>
using namespace std;
void swap(int x, int y); //函数原型int main(){ int a=15, b=18;swap (a, b);cout<<"a="<<a<<" b="<<b<<endl;}void swap(int x, int y)
{ int temp; cout<<"x="<<x<<" y="<<y<<endl;temp=x; x=y; y=temp;cout<<"x="<<x<<" y="<<y<<endl;
}
输出 x= y=
x= y=
a= b=
#include <bits/stdc++.h>
using namespace std;
void swap(int &x, int &y); //函数原型int main(){ int a=15, b=18;swap (a, b);cout<<"a="<<a<<" b="<<b<<endl;}void swap(int &x, int &y) //参数前加&地址符
{ int temp; cout<<"x="<<x<<" y="<<y<<endl;temp=x; x=y; y=temp;cout<<"x="<<x<<" y="<<y<<endl;
}
输出 x= y=
x= y=
a= b=
1、在函数未被调用时,函数的形参并不占有实际的内存空间,也没有实际的值。
2、只有在函数被调用时才为形参分配存储单元,并将实参与形参结合。
3、实参类型必须与形参相符。
4、函数的参数传递指的就是形参与实参结合的过程。
5、函数进行参数传递,是在栈空间里为形参分配空间,并将计算各个实参的表达式的值,然后一一拷贝给形参的过程。
6、根据函数的参数传递的不同,函数调用可以分为:
函数的值传递调用(传值调用)
函数的指针传递调用(传址调用)
函数的引用传递调用(引用调用)
1、传值调用
将函数的实参表达式的值拷贝给形参。这样,实参变量和形参变量占据的是不同的存储空间,因此在函数里对形参变量的改变不会影响到实参变量的值。当函数返回时,形参所占据的存储空间就被系统收回了,也就是说形参变量的值消失了。
这样做的好处是:通过这样的方式将形参和实参隔离,使函数与函数之间的关联减到最低,有利于实现模块化的设计,同时我们也不需要为要不要在调用函数之前保存实参的值而操心。
#include <bits/stdc++.h>
using namespace std;
void swap(int x, int y); //函数原型
int main(){ int a=15, b=18;swap (a, b);cout<<"a="<<a<<" b="<<b<<endl;}
void swap(int x, int y)
{ int t; cout<<"x="<<x<<" y="<<y<<endl;t=x; x=y; y=t;cout<<"x="<<x<<" y="<<y<<endl;
}
在以下情况,使用函数的值传递调用方式不太恰当或者不能达到目的:
1、要传递的参数是一个比较大的自定义的复杂数据类型,如果按值转递方式,需要将这个复杂的数据类型在被调用的函数的栈空间复制一份,这样对时间和空间的开销都比较大。
2、传值调用只能通过return语句返回一个值给函数,如果有时需要在被调用函数里改变实参变量的值。则可以使用函数的指针传递调用的方式和引用传递调用来传递参数。这实际就是向主调函数传递多个值。
二、传址调用
其方法是将函数的形参声明为指针,然后在函数调用时将实参的地址传递给形参。在参数传递时,是将实参的地址复制给形参。 在被调函数里,对形参指向的内存空间进行操作,实际上就是对主调函数里的实参变量进行操作。因为函数的指针传递调用传递的是地址,所以函数的指针传递调用又称为传址调用。 请大家思考,如果要定义一个交换两个变量的值的函数,应该怎么写?
#include <bits/stdc++.h>
using namespace std;
void swap(int *x, int *y); //函数原型
int main(){ int a=15, b=18;swap (&a, &b);cout<<"a="<<a<<" b="<<b<<endl;}
void swap(int *x, int *y)
{ int t; cout<<"x="<<x<<" y="<<y<<endl; cout<<"x="<<*x<<" y="<<*y<<endl;t=*x; *x=*y; *y=t;cout<<"x="<<*x<<" y="<<*y<<endl;
}
三、引用调用
引用:就是某一变量的一个别名,对引用的操作与对变量直接操作完全一样。
引用的声明方法:类型标识符 &引用名=目标变量名;
例:int a; int &x=a; //定义引用ra,它是变量a的引用,即别名
说明:
(1)&在此不是求地址运算,而是起标识作用。
(2)类型标识符是指目标变量的类型。
(3)声明引用时,必须同时对其进行初始化。
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。 x=1; 等价于 a=1;
(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,引用本身不占存储单元。故:对引用求地址,就是对目标变量求地址。&x与&a相等。
(6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
#include <bits/stdc++.h>
using namespace std;
int main()
{ int a;int &x=a;cin>>a; cout<<"a的地址="<<&a<<endl; cout<<"x的地址="<<&x<<endl;cout<<"a的值="<<a<<endl;cout<<"x的值="<<x<<endl;
}
函数也可以通过引用来传递参数。使用引用传递参数与传递指针的效果一样。如果将形参定义为引用,那么当进行参数传递时,实际上是将形参与实参绑定起来,让形参成为实参的别名,而并不是将实参拷贝了一份给形参。因为形参是实参的别名,所以对形参的操作等同于对实参变量的操作。 下面用引用传递的方式来修改Swap函数。
#include <bits/stdc++.h>
using namespace std;
void swap(int &x, int &y); //函数原型
int main(){ int a=15, b=18;swap (a, b);cout<<"a="<<a<<" b="<<b<<endl;}
void swap(int &x, int &y)
{ int temp; cout<<"x="<<x<<" y="<<y<<endl;temp=x; x=y; y=temp;cout<<"x="<<x<<" y="<<y<<endl;
}
传值调用:
方式:函数调用时,实参的数据传递给形参
特点:形参与实参占用不同的存储单元,形参值的改变不影响实参值。 “单向”传递,实参和形参必须是能算出值的表达式
地址传递:
方式: 函数调用时,将实参变量的存储地址作为参数传递给形参
特点: 形参与实参占用同样的存储单元,形参值的改变也改变了实的参值。“双向”传递实参和形参必须是地址常量或变量
十二、局部变量与全局变量
C++语言中每个变量和函数都有数据类型、数据的存储类别和数据作用域三个属性。
变量作用域:按变量在程序中可以使用的范围分为局部变量和全局变量。变量在程序中定义位置决定了该变量在哪一个程序段区间有效,这种性质又称为“可见性”。
数据类型:如整型、字符型等,决定了该变量数据范围以及所占内存单元的大小及形式;
存储类别:规定了该变量所在的存储区域,以及该变量生存期(在内存中保留的时间)可分为: 静态变量和动态变量。
具体可分为四种类型: auto自动型 static静态型 register寄存器型 extern外部型
1、作用域之局部变量
- 局部变量:在一个函数内部或复合语句中定义,只在本函数或复合语句内有效的变量称为局部变量,也称为内部变量。
- 关于局部变量(内部变量)使用的几点说明:
1、在主函数main中定义的变量a,b也是局部变量,而不因为在主函数中定义而在整个程序中有效,主函数也不能使用其它函数中定义的变量。
2、不同函数中可以使用相同的变量名,但它们占用不同的存储单元,互不干扰;
3、形式参数也是局部变量。其它函数是不能调用该形参的;
4、一个函数中,在复合语句中定义的变量只能在本复合语句中有效,如:for(int i=1;i<n;i++)
#include <bits/stdc++.h>
void fund1( void )
{ int nX= 100; //局部变量printf(“nX in fund1=%d\n”, nX);
}
void fund2( void )
{ int nX = 200; //局部变量printf(“nX in fund2=%d\n”, nX);
}
int main( void)
{ int nX =50; //局部变量printf( nX in main=%d\n”, nX);fund1();fund2();
}
本例中定义了三个局部变量nX, 其作用范围分别作用于fund1,fund 和main函数
输出:
nX in main=50
nX in fund1=100
nX in fund2=200
- 不同函数中同名局部变量
#include<bits/stdc++.h>
using namespace std;
void sub()
{ int a,b;a = 6; b = 7;cout<<"sub:a="<<a<<",b="<<b<<endl;
}
int main()
{ int a,b;a = 3; b = 4;cout<<"main:a="<<a<<",b="<<b<<endl;sub();cout<<"main:a="<<a<<",b="<<b<<endl;
}
输出:
main:a=3,b=4
sub:a=6,b=7
main:a=3,b=4
#include<bits/stdc++.h>
#define N 5
using namespace std;
int main()
{ int i;int a[N]={1,2,3,4,5};for(i=0;i<N/2;i++){ int temp;temp = a[i];a[i] = a[N-i-1];a[N-i-1] = temp;}for(i=0;i<N;i++)cout<<a[i];
}
输出:
5 4 3 2 1
- 看程序写答案:
#include<bits/stdc++.h>
using namespace std;
int main( )
{ int a=10,b=50;cout<<"a="<<a<<endl;{int b=20;a=a+b;cout<<"a="<<a<<endl;cout<<"b="<<b<<endl;a=15;}cout<<"a="<<a<<endl;cout<<"b="<<b<<endl;
}
输出:
#include <stdio.h>
int main( )
{ int x=5; //缺省为auto自动型变量printf("x=%d\t",x);if(x>0){ int x=10; //x为复合语句局部(内部)变量printf("x=%d\t",x);}printf("x=%d\n",x+2);
}
输出:
2、作用域之全局变量
- 全局变量:在函数之外定义的变量,从定义变量的位置开始到本源文件结束都有效的变量称为全局变量,也称为外部变量。
- 关于全局变量(外部变量)使用的几点说明
1、若在同一个源文件中,如有全局变量与局部变量同名时,则在局部变量的作用范围内,全局变量不起作用。
2、设置全局变量的作用是: 增加函数间数据联系的渠道。
3、由于同一个文件中的所有函数都能引用全局变量的值,因此如果在一个函数中改变了全局变量的值,就能影响到其它函数,相当于各个函数间有直接的传递通道。一般情况不使用全局变量。
4、它的使用范围: 从定义变量的位置开始到本源程序文件结束
- 看程序写答案
#include <iostream>
using namespace std;
void max( );
int s[5],smax; //定义外部数组和变量
int main()
{ int i;for(i=0;i<5;i++)cin>>s[i];max(); cout<<smax<<endl;
}
void max()
{ int i;smax=s[0];for(i=1;i<5;i++) if(s[i]>smax) smax=s[i];
}
输出:
#include<bits/stdc++.h>
void fun(int p);
int d=1;
main()
{ int a=3; fun(a); d+=a++;printf("%d\n", d);
}
void fun (int p)
{ int d=5; d+=p++;printf("%d\n",d);return;
}
输出:
分析d,a,p值的传递过程
- 全局变量的副作用
#include<bits/stdc++.h>
using namespace std;
int i,j;
main()
{ void prt();for(i=0;i<5;i++){ cout<<i<<endl;prt();}
}
void prt()
{ for(i=0;i<5;i++)cout<<"*"<<i;cout<<endl;
}
输出:
3、总结
十三、变量的存储类别
C++语言中每个变量和函数都有数据类型、数据的存储类别和数据作用域三个属性。
变量作用域:按变量在程序中可以使用的范围分为局部变量和全局变量。变量在程序中定义位置决定了该变量在哪一个程序段区间有效,这种性质又称为“可见性”。
数据类型:如整型、字符型等,决定了该变量数据范围以及所占内存单元的大小及形式;
存储类别:规定了该变量所在的存储区域,以及该变量生存期(在内存中保留的时间)可分为: 静态变量和动态变量。
具体可分为四种类型: auto自动型 static静态型 register寄存器型 extern外部型
变量定义格式: [存储类型] 数据类型 变量表;
如: int sum;
auto int a,b,c;
register int i;
static float x,y;
静态变量:存储区:存储在静态存储区的变量。
生存期:在程序运行期间分配固定的存储空间,不释放
作用域:从程序开始执行到程序结束
初始化:变量值在程序执行中系统只进行一次初始化;
包 括: 全局变量(外部变量)和static局部变量, static全局变量
动态变量:存储区:存储在动态区的变量。
生存期:在程序运行期间根据需要临时分配存储空间,离开即释放
作用域:从包含该变量定义的函数开始执行至函数执行结束
初始化:每次函数调用时都进行初始化;
包 括: auto型局部变量、register型局部变量,函数形参;
1、存储类别之自动变量(auto)
我们以前定义的局部变量就是自动变量
#include<bits/stdc++.h>
using namespace std;
int main()
{ int x=1; //等价于auto int x=1;void prt(void);{ int x=3; //等价于auto int x=3prt();printf(“2nd x=%d\n”,x);}printf(“1st x=%d\n”,x);
}
void prt(void)
{ auto int x=5; //等价于int x=1;printf(“3th x=%d\n”,x);
}
输出:
只给出类型说明的局部变量都默认为auto自动变量。所以定义auto自动变量时可以不写auto;
auto变量包括:
1、函数中定义的局部变量。
2、函数中的形参。
3、在复合语句中定义的局部变量.
而全局变量不能被说明为此类型。
⑴ 内存分配
只有在调用函数或执行分程序时在动态存储区为其分配存储单元,函数或分程序执行结束,所占内存空间即刻释放。
⑵ 变量的初值
定义变量时若没赋初值,变量的初值不确定;如果赋初值则每次函数被调用时执行一次赋值操作。
⑶ 生存期
在函数或分程序执行期间,保存在动态存储区。
⑷ 作用域
自动变量所在的函数内或分程序内。
- 看程序写答案
#include<cstdio>
void fun( )
{ int a=0; //局部auto变量printf("af=%d\n",++a);
}
int main( )
{ int a=10;//局部auto变量fun( ); printf("a=%d\n",++a);fun( ); fun( );
}
输出:
#include<cstdio>
int n;//全局变量没有赋初值,//默认初始化为0;
void show( )
{ auto int i=3;//局部变量n++; //对全局变量操作 i++;printf("n=%d i=%d\n",n,i);{ auto int i=10;i++;printf("now i=%d \n",i);}printf("then i=%d\n",i);
}
int main( )
{ int i;auto int n=1;//与全局变量同名printf("at first n=%d\n",n);for(i=1 ; i<3 ; i++)show( );//main中定义的n无效printf("at last n=%d",n);
}
输出:
2、存储类别之寄存器变量(register)
- 寄存器(register)变量说明:
(1)只有auto变量与形式参数可以作为register变量;
(2)register变量它与auto类变量的区别在于: register变量的值存放在寄存器中而不是在内存中。寄存器是CPU芯片内部的存储器,访问速度极快。常把一些运行速度有较高要求,需要频繁引用的变量定义为register型;
(3)计算机中寄存器的数量是有限的,而且寄存器的数据长度也是有限的。因此register变量不能定义太多,也不能是数据类型太大的变量(long、float、double型);
(4)不能对寄存器变量使用取地址运算符&;
(5)register变量定义通常是不必要的,优化的编译系统能够识别使用频繁的变量,并将其放到寄存器中,而程序员指定的register型变量可能无效。
- 静态变量(static)说明:
(1)内存分配:static变量存放在内存的静态存储区,在整个程序运行期间占用固定的内存单元,程序结束释放该空间。
(2)静态变量的初值:系统在编译时为static变量分配空间并赋初值,对未赋值的局部static数值型变量,系统自动给它赋值为0;对未赋值的局部static字符型变量,自动赋值为空字符。因为在编译时赋初值,所以只能赋一次初值且只能在定义时进行。
(3)生存期:由于变量占用的存储单元不消失,再次调用static局部变量时,static局部变量的值为上次调用结束时的值。
(4)作用域:static局部变量的生存期是整个程序运行期间,但作用域仍然是定义该变量的函数体(或复合语句)内部。 即:静态局部变量在函数调用结束后仍然存在,但其他函数不能引用它。
(5)作用域: static修饰全局变量,作用域是从定义处开始到本源文件结束,将使全局变量作用域局限于本文件内。
(6)可以修饰局部变量和全局变量。
静态局部变量( static局部变量) 定义格式:static int a , b ;
#include<bits/stdc++.h>
void f()
{ static int a; int x=1;a++; x++;printf("a=%d x=%d ",a,x);
}
int main()
{ int i;for(i=0;i<3;i++) f( );
}
输出: a=1 x=2 a=2 x=2 a=3 x=2
分析各变量值的变化过程:
1、定义的静态变量在编译时,系统分赋初值0或空字符。
2、变量占用的存储单元不消失,再次调用static局部变量时,static局部变量的值为上次调用结束时的值。
3、静态局部变量只在本函数或本复合语句中有效,在函数调用结束后仍然存在,但其他函数不能引用它。
本文链接:https://my.lmcjl.com/post/2661.html
4 评论