chapter 5 函數


C語言程序設計

chapter 5 函數

1. 函數初識

1.模塊化編程的含義
將重復性的代碼封裝成一個方法,需要的時候調用這個方法。
一般將這樣的方法稱為函數,完成方法的過程就叫模塊化編程。

2.函數一般划分為自定義函數和系統函數
自定義函數:用戶自己定義函數的功能
系統函數:系統函數就是系統自帶的函數,它的使用需要引入頭文件。

3.函數聲明與定義

函數的聲明:是指告訴編譯器有這樣一種方法,但是至於該方法是否已經實現,並不清楚。

函數的定義:是指對方法的實現,可以省略函數的聲明,但是不能省略函數的定義。

函數定義格式

返回類型  函數名(參數列表){
    函數體
}
示例:
int abs(int num); //函數聲明
int abs(int num){  //函數定義
    if(num<0) return -num;
    else return num;
}

無返回類型  函數名(參數列表){
    函數體
}
示例:
void print(int arr[], int l, int r); //函數聲明
void print(int arr[], int l, int r){  //函數定義
    for(int i=l; i<=r; i++){
        printf("%d ", arr[i]);
    }printf("\n");
}

函數定義須知
(1)函數名是標識符,按照變量名的規則命名。
(2)參數列表可以為空,即無參函數,也可以有多個參數,參數之間用逗號隔開,參數列表中的每個參數,由參數類型說明和參數名組成。
(3)函數體是實現函數功能的語句,返回類型是void的函數無返回值,其余函數執行過程中碰到return語句,將在執行完return語句后直接退出函數,不去執行后面的語句。
(4)返回值的類型一般是int,double,char等類型,也可以是數組。有時候函數不需要返回任何值,這時只需要定義函數返回類型為void。

根據以上須知,可得函數定義格式有如下4種:
(1)返回類型 函數名(參數列表)
(2)返回類型 函數名()
(3)void 函數名(參數列表)
(4)void 函數名()

4.調用方法
根據返回類型可以將函數分為兩大類:有返回值,沒有返回值。
對於有返回值的函數,調用時必須以值的形式出現在表達式中,比如:
int num=abs(-1);
對於沒有返回值的函數,直接寫:函數名(參數);
調用發生在定義之后,直接調用即可;
調用發生在定義之前,則要在調用之前聲明,才可以調用。

2. 形式參數和實際參數

1.形式參數和實際參數的概念
(1)函數定義中的參數名稱為形式參數
如long long C(int n, int m)中的 n與 m是形式參數,形式參數變量名可以替換。
(2)實際參數是指實際調用函數時傳遞給函數的參數的值。
如調用函數 C(6,3),這里 6, 3就是實際參數,其中 6傳遞給 n,3傳遞給m。

2.調用函數的執行過程
(1)計算實際參數的值
(2)把實際參數傳遞給被調用函數的形式參數,程序執行跳到被調用的函數中
(3)執行函數體,執行完后如果有返回值,則把返回值返回給調用該函數的地方繼續執行

3.傳遞參數
函數通過參數來傳遞輸入數據,參數通過傳值機制來實現。
前面的程序中的函數都采用了傳值參數,采用的傳遞方式是值傳遞,函數在被調用時,用克隆實參的辦法得到實參的副本傳遞給形參,改變函數形參的值並不會影響外部實參的值。

#include<stdio.h>

int abs(int num){
    if(num<0) return -num;
    else return num;
}

void print(int arr[], int l, int r){
    for(int i=l; i<=r; i++){
        printf("%d ", arr[i]);
    }printf("\n");
}

int main(){
    int a[]={-1,2,-3,4,-5,6,-7,8,-9};
    int n=sizeof(a)/sizeof(int);
    print(a, 0, n-1);
    for(int i=0; i<n; i++){
        int num=abs(a[i]);
        printf("%d ", num);
    }printf("\n");
    print(a, 0, n-1);
    return 0;
}

輸出結果:
-1 2 -3 4 -5 6 -7 8 -9
1 2 3 4 5 6 7 8 9
-1 2 -3 4 -5 6 -7 8 -9

4.引用參數
函數定義時在變量類型符號之后形式參數名之前加“&”,則該參數是引用參數,把參數聲明成引用參數,實際上改變了缺省的按值傳遞參數的傳遞機制,引用參數會直接關聯到其所綁定的對象,而並非這些對象的副本,形參就是對應形參的別名,形參的變化會保留到實參中。

#include<stdio.h>
void swap(int &a, int &b){//引用,改變的是原地址的值
    int temp=a;
    a=b;
    b=temp;
}
void print(int arr[], int l, int r){
    for(int i=l; i<=r; i++){
        printf("%d ", arr[i]);
    }printf("\n");
}

int main(){
    int a[]={-1,2,-3,4,-5,6,-7,8,-9};
    int n=sizeof(a)/sizeof(int);
    print(a, 0, n-1);
    for(int i=0; i<n; i++){
        for(int j=i+1; j<n; j++){
            if(a[i]>a[j]) swap(a[i], a[j]);
        }
    }
    print(a, 0, n-1);
    return 0;
}

輸出結果:
-1 2 -3 4 -5 6 -7 8 -9
-9 -7 -5 -3 -1 2 4 6 8

3. 作用域

程序中所用到的變量並不總是有效可用的,而限定它可用范圍就是它的作用域。
由於作用域的不同,我們把變量分為兩種:全局變量和局部變量。

1.全局變量
定義在函數外部的變量稱為全局變量。
全局變量的作用域是從變量定義的位置開始到文件結束。
(1)變量x,y定義在所有的花括號外部,具有全局作用域,是全局變量
(2)全局變量的作用域使得函數間多了一種傳遞信息的方式
(3)過多的使用全局變量,會增加調試的難度,會降低程序的通用性
(4)全局變量在函數執行的全過程中一直占用內存單元
(5)全局變量在定義時若沒有賦初值,則為0

2.局部變量
定義在函數內部作用域為局部的變量稱為局部變量。
函數的形參和在該函數內定義的變量都稱為該函數的局部變量。
(1)局部變量只在塊內可見,塊外無法訪問
(2)不同函數的局部變量相互獨立,不能訪問其他函數的局部變量
(3)局部變量的存儲空間是臨時分配的,函數執行完畢,該空間就被釋放
(4)定義在全局作用域中的變量可以在局部作用域中使用,變量還可以在內部作用域中被重新定義,定義在內部作用域的名字將會自動屏蔽定義在外部作用域的相同的名字。

4. 變量的存儲方式和生存期

從變量的作用域(也就是空間),可以將變量分為全局變量和局部變量。
從變量的生存期(也就是時間),可以將變量分為動態存儲方式(自動變量,寄存器變量,形式參數)和靜態存儲方式(靜態局部變量,靜態外部變量,外部變量)。

1.自動變量:auto int a=1; //在動態存儲區內分配內存單元,auto可以省略,省略后隱含指定為"自動存儲類比"
2.靜態變量:static int a=1; //在靜態存儲區內分配內存單元,在整個程序運行期間不釋放。
3.寄存器變量:register int a=1;//在CPU的寄存器中分配內存單元,不用多次存取,提高執行效率
4.外部變量:extern a; //將一定義的外部變量 a的作用域擴展至此

5. 內部函數和外部函數

1.內部函數:又稱靜態函數,只能在本文件中調用:static int fun(int a, int b);
2.外部函數:可以被其它文件調用:extern int fun(itn a, int b); //C語言規定,如果省略 extern 則默認為外部函數。

6. 函數案例練習

題目傳送

【題目描述】在程序中定義一函數 digit(n,k),它能分離出整數 n 從右邊數第 k 個數字(n≤10^9,k≤10)。

輸入格式:正整數 n 和 k。
輸出格式:一個數字。
輸入樣例:31859 3
輸出樣例:8

#include<stdio.h>
int digit(int n, int k){
    for(int i=1; i<k; i++){
        n/=10;
    }
    return n%10;
}
int main(){
    int n,k; scanf("%d%d", &n, &k);
    printf("%d\n", ans);
    return 0;
}

題目傳送

已知:

\[m=\dfrac{\max(a,b,c)}{\max(a+b,b,c) \times \max(a,b,b+c)};|a|,|b|,|c| ≤ 50 \]

輸入 a,b,c, 求 m 。
把求三個數的最大數 max(x,y,z) 分別定義成函數和過程來做。

輸入格式:輸入只有一個行三個整數,分別為 a, b, c.
輸出格式:輸出一行一個小數,為答案,保留三位小數。
輸入樣例:1 2 3
輸出樣例:0.200

#include<stdio.h>
int max(int a, int b, int c){
    int ans=a;
    if(ans<b) ans=b;
    if(ans<c) ans=c;
    return ans;
}
int main(){
    int a,b,c;
    scanf("%d%d%d", &a, &b, &c);
    double ans=1.0*max(a,b,c)/max(a+b,b,c)/max(a,b,b+c);
    printf("%.3lf\n", ans);// %n.mlf
    return 0;
}

題目傳送

【題目描述】求 11 到 n 之間(包括 n),既是素數又是回文數的整數有多少個。
回文數指左右對稱的數,如:11,22,121。

輸入格式:一個大於 11 小於 10000 的整數 n。
輸出格式:11 到 n 之間的素數回文數個數。
輸入樣例:23
輸出樣例:1

#include<stdio.h>
int isprime(int n){
    for(int i=2; i*i<=n; i++){
        if(n%i==0) return 0;
    }
    return 1;
}
int ishuiwen(int n){
    char a[100], cnt=0;
    while(n){
        a[++cnt]=n%10+'0';
        n/=10;
    }
    for(int i=1; i<=cnt/2; i++){
        if(a[i]!=a[cnt-i+1]) return 0;
    }
    return 1;
}
int main(){
    int n; scanf("%d", &n);
    int ans=0;
    for(int i=11; i<=n; i++){
        if(isprime(i) && ishuiwen(i)){
            ans++;
        }
    }
    printf("%d\n", ans);
    return 0;
}

題目傳送

【題目描述】輸入 x,y (1582≤x<y≤3000) ,輸出 [x,y] 區間中閏年個數,並在下一行輸出所有閏年年份數字,使用空格隔開。

輸入樣例:
1989 2001
輸出樣例:
3
1992 1996 2000
#include<stdio.h>
#define N 3000
int ans[N], num=0;
int isleap(int year){
    if(year%400==0 || year%4==0 && year%100!=0) {
        return 1;
    }
    return 0;
}
int main(){
    int x,y; scanf("%d%d", &x, &y);
    for(int i=x; i<=y; i++){
        if(isleap(i)){
            ans[++num]=i; //num=num+1; ans[num]=i;
        }
    }
    printf("%d\n", num);
    for(int i=1; i<=num; i++){
        printf("%d ", ans[i]);
    }
    return 0;
}

7. 函數的遞歸調用

遞歸:何為遞歸?就是不斷的調用自身。

遞歸有兩個特點:
(1)遞歸關系式:對問題進行遞歸形式的描述。
(2)遞歸終止條件(遞歸出口):當滿足該條件時以一種特殊的情況處理,而不是用遞歸關系式來處理。

【題目描述】遞歸實現輸入 n, 輸出 1~n。

遞歸出口:i>n
遞歸關系式:i<=n, 輸出 i

#include<stdio.h>

int n;
void dfs(int i){
    if(i>n) return; //遞歸出口
    printf("%d ", i);
    dfs(i+1);      //繼續遞歸
}

int main(){
    scanf("%d", &n);
    dfs(1);
    return 0;
}

8. 最大公約數與最小公倍數

【題目描述】輸入 a,b, 求他們的最大公約數與最小公倍數。

求最大公約數的三種方法:
1.最小遞減法
先找a,b的最小值,判斷該值能否同時被a,b整除,如果可以該數就是答案,否則每次-1,繼續判斷,直到找到答案。

2.更相減損法
a-b=c,則a,b的最大公約數就是b,c的最大公約數,如果c=0,a就是答案。

3.輾轉相除法
a/b=q...r,則a,b的最大公約數就是b,r的最大公約數,如果r=0,則b就是答案。

輾轉相除法&更相減損法的證明,點擊跳轉

求最小公倍數的兩種方法:
1.最大遞增法
先找a,b的最大值,判斷該值能否同時整除a,b,如果可以該數就是答案,否則每次+1,繼續判斷,直到找到答案。

2.定理法:兩個數的乘積等於這兩個數的最大公約數與最小公倍數的乘積。

#include<stdio.h>

//返回兩個數的最小值
int min(int a, int b){
    return a<b ? a : b;
}

//返回兩個數的最大值
int max(int a, int b){
    return a>b ? a : b;
}

//最小遞減法求最大公約數
int gcd1(int a, int b){
    for(int i=min(a,b); i>=1; i--){
        if(a%i==0 && b%i==0) return i;
    }
    return 1;
}

//通過引用傳參,交換兩個數
void swap(int &a, int &b){
    int temp=a; a=b; b=temp;
}

//更相減損法求最大公約數
int gcd2(int a, int b){
    if(a<b) swap(a, b);
    if(a-b==0) return a;
    return gcd2(b,a-b);
}

//輾轉相除法求最大公約數
int gcd3(int a, int b){
    if(a%b==0) return b;
    return gcd3(b, a%b);
}

//最大遞增法求最小公倍數
int lcm1(int a, int b){
    for(int i=max(a,b); ; i++){
        if(i%a==0 && i%b==0) return i;
    }
}

//定理法:兩個數的乘積等於這兩個數的最大公約數與最小公倍數的乘積
int lcm2(int a, int b){
    return a*b/gcd3(a,b);
}

int main(){
    int a,b; scanf("%d%d", &a, &b);

    printf("gcd1(%d,%d) = %d\n", a, b, gcd1(a,b));
    printf("gcd2(%d,%d) = %d\n", a, b, gcd2(a,b));
    printf("gcd3(%d,%d) = %d\n", a, b, gcd3(a,b));
    printf("lcm1(%d,%d) = %d\n", a, b, lcm1(a,b));
    printf("lcm2(%d,%d) = %d\n", a, b, lcm2(a,b));
    return 0;
}

輸入:12 20
輸出:
gcd1(12,20) = 4
gcd2(12,20) = 4
gcd3(12,20) = 4
lcm1(12,20) = 60
lcm2(12,20) = 60

9. 漢諾塔 Hanoi Tower

【題目描述】法國數學家愛德華·盧卡斯曾編寫過一個印度的古老傳說:
在世界中心貝拿勒斯(在印度北部)的聖廟里,一塊黃銅板上插着三根寶石針。
印度教的主神梵天在創造世界的時候,在其中一根針上從下到上地穿好了由大到小的 64 片金片,這就是所謂的漢諾塔。
不論白天黑夜,總有一個僧侶在按照下面的法則移動這些金片:一次只移動一片,不管在哪根針上,小片必須在大片上面。
僧侶們預言,當所有的金片都從梵天穿好的那根針上移到另外一根針上時,世界就將在一聲霹靂中消滅,而梵塔、廟宇和眾生也都將同歸於盡。

現在有 n 片金片以及ABC三根寶石針,且 n 片金片都穿在 A 針上,問你移動全部金片到 C 針的步驟和移動的次數。
移動 A 最上一片金片到 C,輸出顯示為:move A to C
移動 A 最上一片金片到 B,輸出顯示為:move A to B
移動 B 最上一片金片到 C,輸出顯示為:move B to C
.......

輸入樣例:4
輸出樣例:
move A to B
move A to C
move B to C
move A to B
move C to A
move C to B
move A to B
move A to C
move B to C
move B to A
move C to A
move B to C
move A to B
move A to C
move B to C
15
#include<stdio.h>
int ans=0;
void move(char a, char b, char c, int n){
    if(n==1){
        printf("move %c to %c\n", a, c);
        ans++;
        return; //返回到入口處的下一行
    }
    move(a, c, b, n-1);//將 A中 n-1轉移到 B
    move(a, b, c, 1);  //將 A中剩余 1個轉移到 C
    move(b, a, c, n-1);//將 B中 n-1轉移到 C
}

int main(){
    int n; scanf("%d", &n);
    move('A', 'B', 'C', n);//借助 B,從 A轉移到 C
    printf("%d",ans);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM