函數的引入:
? ? 我們曾經學習了程序設計中的三種基本控制結構(順序、分支、循環)。用它們可以組成任何程序。但在應用中,還經常用到子程序結構。
通常,在程序設計中,我們會發現一些程序段在程序的不同地方反復出現,此時可以將這些程序段作為相對獨立的整體,用一個標識符給它起一個名字,凡是程序中出現該程序段的地方,只要簡單地寫上其標識符即可。這樣的程序段,我們稱之為子程序。
子程序的使用不僅縮短了程序,節省了內存空間及減少了程序的編譯時間,而且有利于結構化程序設計。因為一個復雜的問題總可將其分解成若干個子問題來解決,如果子問題依然很復雜,還可以將它繼續分解,直到每個子問題都是一個具有獨立任務的模塊。這樣編制的程序結構清晰,邏輯關系明確,無論是編寫、閱讀、調試還是修改,都會帶來極大的好處。
????在一個程序中可以只有主程序而沒有子程序(本章以前都是如此),但不能沒有主程序,也就是說不能單獨執行子程序。
在此之前,我們曾經介紹并使用了C++提供的各種標準函數,如abs(),sqrt()等等,這些系統提供的函數為我們編寫程序提供了很大的方便。比如:求sin(1)+ sin(2)+...+sin(100)的值。但這些函數只是常用的基本函數,編程時經常需要自定義一些函數。
例6.1 ?求:1!+2!+3!+……+10!
#include<iostream>
using namespace std;
int main()
{ ??
???????int sum=0;
for (int i=1; i<=10; i++)
sum+=js(i);
cout<<"sum="<<sum<<endl;
return 0;
}
現在的問題是:C++不提供js(x)這樣一個標準函數,這個程序是通不過的。如果是C++的標準函數,我們可以直接調用,如abs(x),sqrt(x)......而C++提供給我們的可供直接調用的標準函數不多。沒關系,我們編寫自己的函數!
函數的定義
1.函數定義的語法形式
數據類型??函數名(形式參數表)
{
函數體????????????????//執行語句
}
關于函數的定義有如下說明:
函數的數據類型是函數的返回值類型(若數據類型為?void ,則無返回值)。
函數名是標識符,一個程序中除了主函數名必須為main外,其余函數的名字按照標識符的取名規則可以任意選取,最好取有助于記憶的名字。
形式參數(簡稱形參)表可以是空的(即無參函數);也可以有多個形參,形參間用逗號隔開,不管有無參數,函數名后的圓括號都必須有。形參必須有類型說明,形參可以是變量名、數組名或指針名,它的作用是實現主調函數與被調函數之間的關系,通常將函數所處理的數據、影響函數功能的因素或者函數處理的結果作為形參。在被調用函數中的參數被稱為形參。
函數中最外層一對花括號“{ }”括起來的若干個說明語句和執行語句組成了一個函數的函數體。由函數體內的語句決定該函數功能。函數體實際上是一個復合語句,它可以沒有任何類型說明,而只有語句,也可以兩者都沒有,即空函數。
函數不允許嵌套定義。在一個函數內定義另一個函數是非法的。但是允許嵌套使用。
函數在沒有被調用的時候是靜止的,此時的形參只是一個符號,它標志著在形參出現的位置應該有一個什么類型的數據。函數在被調用時才執行,也就是在被調用時才由主調函數將實際參數(簡稱實參)值賦予形參。這與數學中的函數概念相似,如數學函數:
f(x)= x 2+x+1
這樣的函數只有當自變量被賦值以后,才能計算出函數的值。
2.函數定義的例子
定義一個函數,返回兩個數中的較大數。
int max(int x,int y)
{
return x>y?x:y;
}
????該函數返回值是整型,有兩個整型的形參,用來接受實參傳遞的兩個數據,函數體內的語句是求兩個數中的較大者并將其返回主調函數。
3.函數的形式
函數的形式從結構上說可以分為三種:無參函數、有參函數和空函數。它們的定義形式都相同。
(1)無參函數
無參函數顧名思義即為沒有參數傳遞的函數,無參函數一般不需要帶回函數值,所以函數類型說明為void。
(2)有參函數
有參函數即有參數傳遞的函數,一般需要帶回函數值。例如
int max(int x,int y)函數。
(3)空函數
空函數即函數體只有一對花括號,花括號內沒有任何語句的函數。
例如,
函數名()
{ ??}
空函數不完成什么工作,只占據一個位置。在大型程序設計中,空函數用于擴充函數功能。
編寫一個階乘的函數,我們給此函數取一個名字js。
int js(int n)
{
int s=1;
for (int i=1; i<=n; ++i)
s*=i;
return s;
}
在本例中,函數名叫js,只有一個int型的自變量n,函數js屬int型。在本函數中,要用到兩個變量i,s。在函數體中,是一個求階乘的語句,n的階乘的值在s中,最后由return語句將計算結果s值帶回,js()函數執行結束,在主函數中js()值就是s的值。
在這里,函數的參數n是一個接口參數,說得更明確點是入口參數。如果我們調用函數:js(3),那么在程序里所有有n的地方,n被替代成3來計算。在這里,3就被稱為實參。又如:sqrt(4),ln(5),這里4,5叫實參。而ln(x),sqrt(x)中的x,y叫形參。
參數傳遞
1.非引用參數
普通的非引用類型的參數是通過復制對應的實參實現初始化。當用參數副本初始化形參時,函數并沒有訪問調用所傳遞的實參本身,因此不會修改實參的值。舉個例子:
#include<iostream>
using namespace std;
void swap(int a,int b)
{
? ? int tmp=a;a=b;b=tmp;
}
int main()
{
? ? ? int c=1,d=2;
? ? ? swap(c,d);
? ? ? cout<<c<<' '<<d<<endl;
? ? ? return 0;
??} ?//程序輸出為:1 2
在此例中,雖然在swap函數中交換了a,b兩數的值,但是在main中卻沒有交換。因為swap函數只是交換c,d兩變量副本的值。
2.引用參數
引用參數直接關聯到其所綁定的對象,而并非這些對象的副本。定義引用時,必須用與該引用綁定對象初始化該引用。引用形參完全以相同的方式工作。每次調用函數,引用形參被創建并與相應實參關聯。現在用引用參數來實現swap:
#include<iostream>
using namespace std;
void swap(int &a,int &b)
{
? ? ? ?int tmp=a;a=b;b=tmp;
}
int main()
{
? ? ? int c=1,d=2;
? ? ? swap(c,d); ???????????//交換變量
? ? ? cout<<c<<' '<<d<<endl;
? ? ? return 0;
} ?//程序輸出為:2 1
在此例中,因為swap函數的參數為引用參數,所以,在函數swap中修改a,b的值相當于在主函數main中修改c,d的值。
3.const形參
使用const修飾參數可避免在函數執行中修改參數。
舉個例子:
???void solve(const int &a)
???{
????a=1;
???} ?????????//該函數是錯誤的,因為a是?const形參,所以在函數體中不能被修改。
使用const修飾參數也可使函數接受常量作為引用參數。
舉個例子:
??void fa(const int &a) { ?}
??void fb(int &a) ?{ ?}
??int main()
??{
fa(2); ???????????//正確,因為fa()使用了常量引用形參。
fb(2); ???????????//錯誤,因為fb()使用了非常量引用形參,不可以接受常量2。
}
函數的聲明和調用
1.函數的聲明
調用函數之前先要聲明函數原型。在主調函數中,或所有函數定義之前,按如下形式聲明:
類型說明符??被調函數名(含類型說明的形參表);
如果是在所有函數定義之前聲明了函數原型,那么該函數原型在本程序文件中任何地方都有效,也就是說在本程序文件中任何地方都可以依照該原型調用相應的函數。如果是在某個主調函數內部聲明了被調用函數原型,那么該原型就只能在這個函數內部有效。
函數原型和函數定義在返回值類型、函數名和參數個數與類型必須完全一致,否則,就會發生編譯錯誤。下面對max()函數原型聲明是合法的。
int max(int x, int y);
也可以:
int max(int , int?);
可以看到函數原型聲明與函數定義時的第一行類似,只多了一個分號,成為了一個聲明語句而已。
2.函數的調用
聲明了函數原型之后,便可以按如下形式調用函數:
函數名(實參列表)
實參列表中應給出與函數原型形參個數相同、類型相符的實參。在主調函數中的參數稱為實參,實參一般應具有確定的值。實參可以是常量、表達式,也可以是已有確定值的變量,數組或指針名。函數調用可以作為一條語句,這時函數可以沒有返回值。函數調用也可以出現在表達式中,這時就必須有一個明確的返回值。
3.函數的返回值
在組成函數體的各類語句中,值得注意的是返回語句return。它的一般形式是:
return(表達式);
其功能是把程序流程從被調函數轉向主調函數并把表達式的值帶回主調函數,實現函數的返回。所以,在圓括號表達式的值實際上就是該函數的返回值。其返回值的類型即為它所在函數的函數類型。當一個函數沒有返回值時,函數中可以沒有return語句(在TC++和VC++,函數類型定義為void,可以沒有return語句;函數類型定義為int,必須有返回值),直接利用函數體的右花括號“}”,作為沒有返回值的函數的返回。也可以有return語句,但return后沒有表達式。返回語句的另一種形式是:
return;
這時函數沒有返回值,而只把流程轉向主調函數。
全程變量、局部變量及它們的作用域
在函數外部定義的變量稱為外部變量或全局變量,在函數內部定義的變量稱為內部變量或局部變量。
1.全局變量
全局變量的作用域是從變量定義的位置起直至本源文件結束止,即從定義位置之后的所有函數都可以訪問該全局變量。
下面通過例子來說明這一點。
??例??全局變量的應用。
#include<cstdio>
#include<iostream>
using namespace std;
int x,y; ??????????//定義全局變量x,y
int fun1(int s)
{ ???????????????????//訪問全局變量x,y
????x=10;
????y=x*s;
????return x+y;
}
float a,b; ??????//定義全局變量a,b
void fun2(int c)
{
????cout<<"x="<<x<<" y="<<y<<endl;??//訪問全局變量x,y
}
int main()
{
????int m,n;
????cin>>m>>n;
????cout<<fun1(m)<<endl;
????fun2(n);
cout<<"a="<<a<<" ?b="<<b<<endl;????//訪問全局變量a,b
return 0;
}
當在鍵盤上輸入6 9后程序的執行結果是:
70
x=10 ?y=60
a=0 ?b=0
????程序中主函數先調用fun1(),實參是6,將實參值傳給形參s(即s=6),函數fun1中x值為10,y值為60,return語句將x+y的和返回,fun1()結束。主函數輸出返回值之后,調用fun2(),在fun2()中輸出全局變量x和y?的值,之后返回主函數。在主函數中輸出全局變量a和b值。由于a、b在聲明時未賦初值,系統的默認值為0。
使用全局變量的說明:
在一個函數內部,既可以使用本函數定義的局部變量,也可以使用在此函數前定義的全局變量。
全局變量的作用是使得函數間多了一種傳遞信息的方式。如果在一個程序中多個函數都要對同一個變量進行處理,即共享,就可以將這個變量定義成全局變量,使用非常方便,但副作用也不可低估。
過多地使用全局變量,會增加調試難度。因為多個函數都能改變全局變量的值,不易判斷某個時刻全局變量的值。
過多地使用全局變量,會降低程序的通用性。如果將一個函數移植到另一個程序中,需要將全局變量一起移植過去,同時還有可能出現重名問題。
全局變量在程序執行的全過程中一直占用內存單元。
全局變量在定義時若沒有賦初值,其默認值為0。
2.局部變量
⑴局部變量的作用域是在定義該變量的函數內部。換句話說,局部變量只在定義它的函數內有效。在一個子程序內定義的變量也是局部變量,其作用域是該子程序。函數的形參也是局部變量。
⑵由于局部變量的作用域僅局限于本函數內部,所以,在不同的函數中變量名可以相同,它們分別代表不同的對象,在內存中占據不同的內存單元,互不干擾。
⑶一個局部變量和一個全局變量是可以重名的,在相同的作用域內局部變量有效時全局變量無效。即局部變量可以屏蔽全局變量。
題目:
質數?查看測評數據信息
輸入一個正整數(30000以內)a,如果a是素數,輸出yes,否則輸出no;
素數,又叫質數:一個整數如果只能被1和它本身整除,那么這個整數便是質數。注意:1不是素數,要求用子函數書寫代碼。
輸入格式
一個正整數。
輸出格式
如果a是素數,輸出yes,否則輸出no;
輸入/輸出例子1
輸入:
11
輸出:
yes
#include<bits/stdc++.h>
using namespace std;
int n,c=0;
int main(){cin>>n;if(n==1){cout<<"no";return 0;} for(int i=2;i*i<=n;i++){c=0;if(n%i==0){c=1;cout<<"no";break;}}if(c==0)cout<<"yes";return 0;
}
?階乘之和?查看測評數據信息
我們在數學上把從1開始的連續自然數相乘叫做階乘。例如把1*2*3*4*5稱作5的階乘,記為5!,你能寫一個自定義函數或者過程來求n!嗎?調用這個函數求出10個以內階乘數之和。
【數據規模】 n<=10
輸入格式
第一行輸入n,表示n個數。
第二行輸入n個要計算的階乘數(10以內)。
輸出格式
輸出n個階乘之和。
輸入/輸出例子1
輸入:
2
3
2
輸出:
8
#include<bits/stdc++.h>
using namespace std;
int main(){int n,a,s=0;cin>>n;for(int i=1;i<=n;i++){cin>>a;int x=1;for(int j=1;j<=a;j++){x=x*j;}s=s+x;}cout<<s;return 0;
}
數學問題?查看測評數據信息
? ? ? ? 在一個渺無人煙的荒島上待了XX年之后,小z基本上啥都不會了。所以,當小y告訴他任何一個大于等于4的數都能表示成兩個質數的和這個事實的時候,小z根本不相信!小z現在想找出一些反例,你能幫助他嗎?
輸入格式
輸入文件第一行為一個整數n(1<=n<=50)
接下來有n行,每行包含一個整數m。(3<=m<=1000000)
輸出格式
輸出文件共n行,每行對應于每一個m,如果m不能表示成兩個質數的和,則輸出“NO WAY!”;否則輸出一種方案。如果有多種可行方案,輸出兩個質數的差最大的那一種。
輸入/輸出例子1
輸入:
2
10
11
輸出:
10=3+7
NO WAY!
#include<bits/stdc++.h>
using namespace std;
long long n,a;
bool zs(int x){if(x==1)return 0;for(int i=2;i*i<=x;i++)if(x%i==0)return 0;return 1;
}
int main(){cin>>n;for(int i=1;i<=n;i++){cin>>a;bool f=0;for(int j=1;j<=a/2;j++){if(zs(j)&&zs(a-j)){f=1;cout<<a<<"="<<j<<"+"<<a-j<<endl;break;}}if(f==0)cout<<"NO WAY!"<<endl;}return 0;
}
統計閏年?查看測評數據信息
輸入兩個年份 x 和 y,統計并輸出公元 x 年到公元 y 年之間的所有閏年數(包括 x 年和 y 年),1≤x≤y≤3000。
(要求,寫一個判斷i是不是閏年的子函數)
輸入格式
一行兩個正整數表示 x 和 y,之間用一個空格隔開。
輸出格式
一行兩個正整數表示 x 和 y,之間用一個空格隔開。
輸入/輸出例子1
輸入:
2000 2004
輸出:
2
#include<bits/stdc++.h>
using namespace std;
int x,y,s,i;
int main(){cin>>x>>y;for(i=x;i<=y;i++)if((i%4==0&&i%100!=0)||i%400==0)s++; cout<<s;return 0;
}
組合問題?查看測評數據信息
Cmn表示從n個物品中選出m個物品的方案數。舉個例子,從(1,2,3) 三個物品中選擇兩個物品可以有(1,2),(1,3),(2,3)這三種選擇方法。根據組合數的定義,我們可以給出計算組合數的一般公式:

其中:n!=1?2?3?....?n
對于給定的n、m,求Cmn的值。
請定義函數JC(int k); 求指定整數的階乘,并在主函數中調用該函數計算Cmn的值。
輸入格式
只有一行,兩個數n和m(1<=m<=n<=20)。
輸出格式
只有一個數,為總選法。
輸入/輸出例子1
輸入:
5 3
輸出:
10
#include<bits/stdc++.h>
using namespace std;
long long JC(int k){ long long ans=1;for(int i=2;i<=k;i++)ans*=i;return ans;
}
int main(){int n,m;cin>>n>>m;long long ans=JC(n)/(JC(m)*JC(n-m));cout<<ans;return 0;
}
回文素數?查看測評數據信息
桐桐在研究素數時,發現有些素數很特別,例如131,它是素數,同時,它又是回文數:從左邊讀和從右邊讀都是同一個數。桐桐想把不大于n的所有既是回文數又是素數的數求出來,你能幫助她嗎?
要求:寫一個函數f1判斷一個整數是否為質數,寫一個函數f2判斷一個整數是否為回文數。
輸入格式
只有一個整數n(2≤n≤10^6)。
輸出格式
輸出滿足題設條件的數,每行輸出5個數。
輸入/輸出例子1
輸入:
100
輸出:
2 3 5 7 11
#include<bits/stdc++.h>
using namespace std;
int n,ans=0;
int sb(int x){int t=x,s=0;while(t>0){s=s*10+t%10;t=t/10;}if(s==x)return 1;return 0;
}
int zx(int x){if(x==1)return 0;for(int i=2;i*i<=x;i++){if(x%i==0)return 0;}return 1;
}
int main(){cin>>n;int k=0;for(int i=1;i<=n;i++){if(sb(i)){if(zx(i)){cout<<i<<" ";k++;if(k%5==0)cout<<endl;}}}return 0;
}