C語言(六)
阿新 • • 發佈:2018-11-24
第六章 函式
- 6.1 函式概述
- 定義
- 函式:是具有一定功能的一個程式塊;是C語言的基本組成單位。
- 在前面各章的例子及讀者自己編寫的C語言程式中都用到了以“main“開頭的主函式,並且在程式中頻繁地呼叫了C語言提供的用於輸入輸出的庫函式( scanf( )和printf( )函式)。
- 函式是C源程式的基本模組,通過對函式模組的呼叫實現特定的功能。
- 一個C程式可由一個主函式和若干個其他函式構成,並且只能有一個主函式。由主函式來呼叫其他函式,其他子函式之間也可以互相呼叫。
- C程式的執行總是從main()函式開始。呼叫其他函式完畢後,程式流程回到main( )函式,繼續執行主函式中的其他語句,直到main( )函式結束,則整個程式的執行結束。
- main( )函式是由系統定義的。所有的函式都是平行的,即在函式定義時它們是互相獨立的,函式之間並不存在從屬關係。也就是說,函式不能巢狀定義(這是與PASCAL不同的),函式之間可以互相呼叫,但不允許呼叫main( )函式。
- 分類
- 從使用者的使用的角度看,函式有兩種:
- (1) 標準函式,即庫函式。這些函式由系統提供,可以直接使用。
- (2) 自定義的函式。用以解決使用者需要時設計定義的函式。
- 從函式的形式看,函式分為兩類:
- (1)無參函式。
- (2)有參函式。
- (1)無參函式。
- 從使用者的使用的角度看,函式有兩種:
- 定義
- 6.2 函式定義的一般形式
- 函式的定義
- C語言中函式定義的一般形式如下:
- 函式返回值的型別名 函式名(型別名 形式引數1,型別名 形式引數2, … )
- {
- 說明部分;
- 語句部分;
- }
- C語言中函式定義的一般形式如下:
- 說明:
- 函式名和各個形式引數都是由使用者命名的合法識別符號,與普通變數名的定義規則相同。在同一程式中,函式名必須唯一,不能出現重名的情況。形式引數名只要在同一函式中唯一即可,由於形式引數的作用域不相同,因此形式引數名可以與其他函式中的變數名同名。C語言規定,不能在一個函式內部再定義函式,也就是說函式不能巢狀定義。
- 若在函式的首部省略了函式返回值的型別名,可以把函式首部寫成:
- 函式名(型別名形式引數1 ,型別名 形式引數2 ,…,型別名 形式引數n)
- 緊跟在函式名之後的圓括號中的內容是形式引數和型別說明表,在每個形參之前都要有型別名,以標識形式引數的型別。各形參的定義之間用逗號分隔。
- 例如,求兩整數和的函式:
- int add(int a ,int b)
- {
- intt; /* 函式體中宣告部分 */
- t=a+b;
- return t;
- }
- 例如,求兩整數和的函式:
- 若所定義的函式沒有形參,函式名後的一對圓括號依然不能省略。本例中函式體中的語句是用來完成求和的功能。在某些情況下,函式體可以是空的,例如:
- fun()
- { }
- 該函式中沒有任何語句,什麼工作也不做,沒有任何實際作用。之所以要在主調函式上這樣寫,是為了表明此處要呼叫一個函式,而現在這個函式的具體功能可能還沒有設計好,沒有起作用,等以後擴充函式功能時補充上即可。
- 在函式體中,除形參外,用到的其他變數必須在說明部分進行定義,這些變數(包括形參)只在函式被呼叫時才被臨時分配記憶體單元,當退出函式時,這些臨時開闢的儲存單元全部被釋放掉,即在該函式體內部定義的變數都將不存在。因此,這些變數只在函式體內部起作用,與其他函式體內的變數並不相關。
- 函式名和各個形式引數都是由使用者命名的合法識別符號,與普通變數名的定義規則相同。在同一程式中,函式名必須唯一,不能出現重名的情況。形式引數名只要在同一函式中唯一即可,由於形式引數的作用域不相同,因此形式引數名可以與其他函式中的變數名同名。C語言規定,不能在一個函式內部再定義函式,也就是說函式不能巢狀定義。
- 函式的定義
- 6.3 函式引數和函式返回值
- 6.3.1 形式引數和實際引數
- 定義
- 在程式中呼叫函式時,絕大多數情況下,主調函式和被調函式之間會發生資料傳遞關係,這就要用到前面提到的有參函式。在定義函式時,函式名後面括號中的變數稱為“形式引數“(簡稱“形參“);在主調函式中,函式名後面括號中的引數(可以是一個表示式)稱為“實際引數“(簡稱“實參“)。
- 說明:
- (1)實參可以是常量、變數或表示式。
- (2)在被定義的函式中必須指定形參型別。
- (3)實參與形參的型別應相同或賦值相相容。
- (4)C語言規定,實參變數對形參變數的資料傳遞是“值傳遞“,即單向傳遞。只能由實參傳給形參,而不能由形參返回來給實參。在記憶體中,實參單元與形參單元是不同的單元。
- (5)在呼叫函式時,給形參分配儲存單元,並將實參對應的值傳遞給形參。呼叫結束後,形參單元被釋放,實參單元仍保留並維持原值。
- (6)一定要注意引數之間的傳遞,實參和形參之間 傳數值,和傳地址的差別。傳數值的話,形參的變化不會改變實參的變化。傳地址的話,形參的變化就會有可能改變實參的變化。
- 定義
- 6.3.2 函式的返回值
- 函式的返回值就是通過函式呼叫使主調函式能得到一個確定的值。函式的值通過return語句返回,return語句的形式如下:
- return 表示式;
- 或return(表示式);
- 或return;
- return 語句中的表示式的值就是所求的函式值。此表示式值的型別必須與函式首部所說明的型別一致。若型別不一致,則以函式值的型別為準,由系統自動進行轉換。
- 例如 通過函式呼叫的方法求1到自然數n(n>1)自然數的和,有程式段如下
- #include<stdio.h>
- ints(int n)
- {
- int i,sum=0;
- for(i=1;i<=n;i++)
- sum+=i;
- return sum;
- }
- main()
- {
- int n;
- printf("input number\n");
- scanf("%d",&n);
- n=s(n);
- printf("1到n的和為:%d\n",n);
- }
- 例如 通過函式呼叫的方法求1到自然數n(n>1)自然數的和,有程式段如下
- 函式的返回值就是通過函式呼叫使主調函式能得到一個確定的值。函式的值通過return語句返回,return語句的形式如下:
- 6.3.1 形式引數和實際引數
- 6.4 函式的呼叫
- 6.4.1 函式呼叫的一般形式
- 函式名(實參表列);
- 函式的呼叫可以分為呼叫無參函式和呼叫有參函式兩種,如果是呼叫無參函式,則不用“實參表列“,但括號不能省略。在呼叫有參函式時,若實參列表中有多個實參,各引數間用逗號隔開。實參與形參要求型別一致。
- 6.4.2 函式呼叫的方式
- (1)函式語句。把函式呼叫做為一個語句,這時該函式只需要完成一定的操作而不必有返回值。
- (2)函式表示式。當一個函數出現在一個表示式中,該表示式就被稱為函式表示式。因為要參與表示式中的計算,所以要求該函式有一個確定的返回值提供給表示式。
- (3)函式引數。函式呼叫做為一個函式的實參。
- 6.4.3 C語言中,呼叫函式和被呼叫函式之間的資料可通過3種方式進行傳遞。
- (1)實參與形參之間進行資料傳遞。
- (2)通過return語句把函式值返回到主呼叫函式中。
- (3)通過全域性變數。
- 6.4.3 函式的遞迴呼叫
- 函式的遞迴呼叫一定要記得有結束的條件
- 在呼叫一個函式的過程中又出現直接或間接地呼叫該函式本身,稱為函式的遞迴呼叫。允許函式的遞迴呼叫是C語言的特點之一。
- 當一個問題在採用遞迴法解決時,必須符合以下3個條件:
- (1)可以把要解決的問題轉化為一個新的問題。而這個新的問題的解決方法仍與原來的解決方法相同,只是所處理的物件有規律地遞增或遞減。
- (2)可以應用這個轉化過程使問題得到解決。
- (3)必須要有一個明確的結束遞迴的條件。
- 當函式自己呼叫自己時,系統將自動把函式中當前的變數和形參暫時保留起來,在新一輪的呼叫過程中,系統將為本次呼叫的函式所用到的變數和形參,開闢新的儲存單元。因此,遞迴呼叫的層次越多,同名變數所佔的儲存單元也就越多。當本次呼叫的函式執行結束時,系統將釋放本次呼叫所佔的儲存單元。當程式執行的流程返回到上一層的呼叫點時,同時取用進入該層函式中的變數和形參所佔用的儲存單元中的資料。
- 例如 求n!的值,有程式段如下:
- #include<stdio.h>
- longff(int n)
- {
- long f;
- if(n<0)
- printf("n<0,input error");
- else if(n==0||n==1) f=1;
- else f=ff(n-1)*n;
- return(f);
- }
- main()
- {
- int n;
- long y;
- printf("請輸入整數值:\n");
- scanf("%d",&n);
- y=ff(n);
- printf("%d!=%ld",n,y);
- }
- 6.4.1 函式呼叫的一般形式
- 6.5 函式的說明
- 6.5.1 形式
- 概念
- C語言中,除了主函式外,對於使用者定義的函式要遵循先定義後使用的規則。把函式的定義放在呼叫之後,應該在呼叫之前對函式進行說明(或函式原型說明)。
- 函式說明的一般形式如下:
- 型別名 函式名(引數型別1 ,引數型別2 ,…,引數型別n);
- 如
- intadd(int ,int );
- 如
- 或
- 型別名 函式名(引數型別1 引數名1,引數型別2 引數名2 ,…,引數型別n 引數名n);
- 此處的引數名完全是虛設的,它們可以是任意的使用者識別符號,既不必與函式首部中的形參名一致,又可以與程式中的任意使用者識別符號同名,實際上引數名常常省略。函式說明語句中的型別名必須與函式返回值的型別一致。
- 型別名 函式名(引數型別1 ,引數型別2 ,…,引數型別n);
- 函式說明可以是一條獨立的語句。對函式進行說明,能使C語言的編譯程式在編譯時進行有效的型別檢查。當呼叫函式時,若實參的型別與形參的型別不能賦值相容而進行非法轉換,C編譯程式將會發現錯誤並報錯;當實參的個數與形參的個數不同時,編譯程式也將報錯。
- 概念
- 6.5.2函式說明的位置
- 一個函式在所有函式的外部,如在被呼叫之前說明,則在說明後的所有位置上都可以對該函式進行呼叫。如在main()函式內部進行說明,則只能在main()函式內部才能識別。
- 例如 呼叫求和函式輸出1到n的和值,程式段如下:
- #include<stdio.h>
- main()
- {
- int n;
- int s(int n);
- printf("input number\n");
- scanf("%d",&n);
- s(n);
- printf("n=%d\n",n);
- }
- ints(int n)
- {
- int i,sum=0;
- for(i=1;i<=n;i++)
- sum+=i;
- printf("1到n的和值為:%d\n",n);
- }
- 一定要有:函式名,函式的返回型別,函式的引數型別。不一定要有:形參的名稱。
- 6.5.1 形式
- 6.6 常用函式
- 6.6.1 字串 <string.h>
- 6.6.1.1 strlen 計算長度
- 6.6.1.2 strcmp 比較
- 6.6.1.3 strcpy 複製 (不安全,越界)
- 6.6.1.4 strcat 追加 (不安全,越界)
- 6.6.2 數學函式<math.h>
- 6.6.2.1 向上,向下取整
- 函式名: ceil
- 功能:向上取整
- 用法: double ceil(double x);
- 函式名: floor
- 功能:向下取整
- 用法: double floor(double x);
- 函式名: ceil
- 6.6.2.2 取絕對值
- 函式名:abs
- 功能:返回整型數的絕對值.
- 用法:abs(number)
- number 引數可以是任意有效的數值表示式。如果 number 包含 Null,則返回Null;如果是未初始化變數,則返回 0.
- 函式名:fabs
- 功能:求浮點數x的絕對值.
- 用法:fabs (double x);
- 函式名:abs
- 6.6.2.3 平方根
- 函式名:sqrt
- 功能:返回指定數字的平方根.
- 用法:sqrt (double x);
- 函式名:sqrt
- 6.6.2.4 求冪
- 函式名:exp
- 功能:返回 e 的 n 次冪.
- 用法:exp (double x);
- 函式名:pow
- 功能:返回指定數字的指定次冪.
- 用法:pow (double x, double y);(將返回x的y次冪)
- 函式名:exp
- 6.6.2.5 取餘
- 函式名: fmod
- 功 能: 計算x對y的模, 即x/y的餘數
- 用 法:double fmod(double x, double y);
- 函式名: fmod
- 6.6.2.6 對數
- 函式名:log
- 功能: 自然對數函式ln(x)
- 用法: double log(double x);
- 函式名:log10
- 功能:返回以 10 為底的對數.
- 用法:log10(double x);
- 函式名:log
- 6.6.2.7 三角函式:(所有引數必須為弧度)
- sin
- 函式宣告:sin (double x);
- 用途:用來返回給定的 X 的正弦值。
- cos
- 函式宣告:cos (double x);
- 用途:用來返回給定的 X 的餘弦值。
- tan
- 函式宣告:tan (double x);
- 用途:用來返回給定的 X 的正切值。
- sin
- 6.6.2.8 反三角函式
- acos
- 函式申明:acos (double x);
- 用途:用來返回給定的 X 的反餘弦函式。
- asin
- 函式申明:asin (double x);
- 用途:用來返回給定的 X 的反正弦函式。
- atan
- 函式申明:atan (double x);
- 用途:用來返回給定的 X 的反正切函式。
- atan2
- 函式宣告:atan2 (double y, double x);
- 用途:返回給定的 X 及 Y 座標值的反正切值
- acos
- 6.6.2.9 雙曲函式
- 函式名:cosh
- 功能:返回指定角度的雙曲餘弦值.
- 用法:Double Cosh(double x(以弧度計量的角度)) ;
- 函式名:sinh
- 功能:返回指定角度的雙曲正弦值。
- 用法:sinh (double x);(其中引數x必須為弧度制)
- 函式名:tanh
- 功能:回指定角度的雙曲正切值.
- 用法:tanh (double x);
- 函式名:cosh
- 6.6.2.10 實型數分整數和小數
- 函式名:modf
- 功 能: 把數分為整數部分和小數部分
- 用 法: double modf(doublevalue, double *iptr);
- eg
- 1. #include<math.h>
- 2.
- 3. #include<stdio.h>
- 4.
- 5. int main(void)
- 6.
- 7. {
- 8.
- 9. double fraction,integer;
- 10.
- 11. double number =100000.567;
- 12.
- 13. fraction =modf(number, &integer);
- 14.
- 15. printf("Thewhole and fractional parts of %lf are %lf and %lf\n",
- 16.
- 17. number, integer,fraction);
- 18.
- 19. return 0;
- 20.
- 21. }
- The whole andfractional parts of 100000.567000 are 100000.000000 and 0.567000
- 函式名:modf
- 6.6.2.11 隨機數
- 在程式設計的時候需要電腦來獲取一些隨機的反應,這個時候我們可以使用隨機數,比較常見的是 rand() 函式,它可以隨機的產生 0 ~rand_max 的隨機數。rand_max 是一個很大的數字,具體關係到IDE和資料型別,我們一般的需要不可能超出它的範圍
- C語言中還有一個 random() 函式可以獲取隨機數,但是 random() 函式不是ANSI C標準,不能在VC等編譯器通過,所以比較少用。
- eg
- int a=rand()%10; //產生0~9的隨機數,注意10會被整除
- int a=rand()%51+13; //產生13~63的隨機數
- 產生 13~63 範圍內隨機數的完整程式碼:
- 1. #include <stdio.h>
- 2. #include <stdlib.h>
- 3. #include <time.h>
- 4. int main(){
- 5. int a;
- 6. srand((unsigned)time(NULL));
- 7. a=rand()%51+13;
- 8. printf("%d\n",a);
- 9. return 0;
- 10. }
- 下面是一個例項:
- 1. #include <stdio.h>
- 2. #include <stdlib.h>
- 3. int main(){
- 4. int a=rand();
- 5. printf("%d\n",a);
- 6. return 0;
- 7. }
- 編譯後再執行幾次,你會發現產生的隨機數是相同的。實際上,rand() 函式產生的隨機數是偽隨機數,是根據一個數按照某個公式推算出來的,這個數我們稱之為“種子”,但是這個種子在系統啟動之後就是一個定值,我們需要用 srand() 來進行播種,即在int a前加一句:
- 1. srand((unsigned)time(NULL)); //這裡利用時間進行播種,需要time.h
- sqrt( ) fabs( ) pow( ) sin( )
- 6.6.2.1 向上,向下取整
- 6.6.3 malloc
- malloc的返回型別是 void *
- int *p;
- p = (int *)malloc(2);
- p = (int *)malloc(sizeof(int));以上兩個等價
- malloc的返回型別是 void *
- 6.6.1 字串 <string.h>