一、函數概述
1、首先什麼是函數?
函數是用於完成特定任務的程序代碼的自包含單元。
2、爲什麼使用函數?
第一、函數的使用可以身故重複代碼的編寫。第二、函數使得程序更加模塊化,有利於程序的閱讀修改和完善。
3、main函數原型
int main (int argc, char * argv[], char * envp[]) {....}
第一個參數:命令行參數的個數
第二個參數:命令行參數的地址信息
第三個參數:環境表的首地址
參數argc表示輸入參數的個數(含命令名),如果有命令行參數,argc應不小於1;argv表示傳入的參數的字符串,是一個字符串數組,argv[0]表示命令名,argv[1]指向第一個命令行參數;至於第三個參數env,它與全局變量environ相比也沒有帶來更多益處,所以POSIX.1也規定應使用environ而不使用第三個參數。通常用getenv和putenv函數來存取特定的環境變量,而不是直接使用environ變量。例如:
//main函數原型的使用
#include <stdio.h>
int main(int argc,char* argv[],char* envp[])
{
printf("argc=%d",argc);
int i=0;
for(i=0;i<argc;i++)
{
printf("感謝%s",argv[i]);
}
//聲明全局變量
extern char** environ;
printf("environ=%p,envp=%p",environ,envp);//直接訪問環境表
return 0;
}
輸出結果爲:
argc=1
再如:
命令行爲:myprogram c:a.txt c:.txt
則:argc==3
argv[0]=="myprogram"
argv[1]=="c:\a.txt"
argv[2]=="c:\b.txt"
再再如:
#include <stdio.h>
int main (int argc, char *argv[])
{
int count;
printf ("The command line has %d arguments: ", argc - 1);
for (count = 1; count < argc; count++)
printf ("%d: %s", count, argv[count]);
printf ("");
return 0;
}
輸出結果:
tarena@ubuntu:~/project/C$ ./a.out I LOVE YOU
The command line has 3 arguments:
1: I
2: LOVE
3: YOU
每個C程序都必須有一個 main 函數,因爲它是程序執行的起點。關鍵字 int 表示函數返回一個整型值,關鍵字 void 表示函數不接受任何參數。main 函數的函數體包括左花括號和與之匹配的右花括號之間的任何內容。
注意:如果沒有寫明返回值,一般默認爲 int 類型。
main (void)
{
...
return 0;
}
二、函數聲明和定義 可以加下小編的羣466572167,可以交流學習,也有資料下載
1、函數聲明
在任何程序在使用函數之前都需要聲明該函數的類型。
例如:int imax (int a, int b);
第一個int指的是函數類型;位於圓括號內的兩個int表明該函數的形式參數爲int類型。分號的作用表示該語句是進行函數聲明而不是函數定義。
在函數原型中可以根據自己的喜好省略變量名。
int imax (int , int);
需要注意的是這些變量名只是虛設的名字,他們不必和函數定義中使用的變量名想匹配。使用這種函數原型信息,編譯器就可以檢查函數調用語句是否和其原型聲明一致。比如檢查參數個數是否正確,參數類型是否匹配。將聲明語句放置在main ()之前和之內兩種方式都是正確的。需要重點注意的是函數聲明要在使用函數之前進行。當在main之前函數定義的話,就不需要聲明語句了。
#include <stdio.h>
int imin (int, int);
int main (void)
{
int inim (int, int);
}
2、隱式聲明
如果被調用函數寫在調用函數後面則編譯器會猜測被調用函數的格式,這叫做函數的隱式聲明。隱式聲明可以和實際情況不一致這時就會出錯,可以把被調用函數的聲明單獨寫在文件開頭,這叫做函數的顯示聲明。除了主函數以外的所有函數都應該進行顯示聲明,因爲主函數永遠不是被調用函數。如果main()之後又函數定義,main()之前卻沒有函數聲明的話,編譯的時候會出現隱式聲明報錯。
3、頭文件的使用
如果把main ()函數放在第一個文件中吧自定義哈拿書放在第二個文件中實現,那麼第一個文件仍需要使用函數原型。如果把函數原型放在一個頭文件中,就不必每次使用這些函數時輸入其原型聲明瞭。
#ifndef __02READ_H__
#define __02READ_H__
extern int num; //主函數全局變量寫在了頭文件裏且要加extern關鍵字
int read(char, int); //函數聲明
#define PI 3.14
#endif
4、函數定義
函數聲明只是將函數類型告訴編譯器,而函數定義部分則是函數的實際實現代碼。之所以使用函數原型,是爲了在編譯器編譯第一個調用函數的語句之前想其表明該函數的使用方法。因此,可以在首次調用某函數之前對該函授進行完整的定義。這樣函數定義部分就和函數原型有着相同的作用。通常對較小的函數會這樣做:
//下面既是一個函數的定義,也是它的原型
#include <stdio.h>
int imax (int a, int b)
{
return a > b ? a : b;
}
int main (void)
{
int x, z;
scanf ("%d", &x);
z = imax (x, 50);
printf ("%d", z);
return 0;
}
三、函數形參與實參 可以加下小編的羣466572167,可以交流學習,也有資料下載
1、形式參數
函數定義爲下面的函數:
void show (char ch, int num)
這行代碼通知編譯器show()使用名爲ch和num的兩個參數,並且這兩個參數的類型分別爲char和int。變量ch和num被稱爲形式參數。
注意:ANSI C形式要求在每個變量前聲明其類型。也就是說,不能像通常變量聲明那樣使用變量列表聲明同一類型的變量。如下所示:
void dibs (int x, y, z) /*不正確的函數頭*/
void dibs (int x, int y, int z) /*正確的函數頭*/
2、實際參數
函數調用中,通常使用實際參數對ch和num賦值,如:
show (SPACE, 12);
形式參數是被調函數中的變量,而實際參數是調用函數分配給被調用函數變量的特定數值。實際參數可以是常量、變量或一個複雜的表達式。但是無論何種形式的實際參數,執行時首先要計算其值,然後將該值複製給被調用函數中相應的形式參數。
3、無參數
void dibs (void);
爲了表示一個函數確實不使用參數,需要在圓括號內加入void關鍵字。
四、遞歸函數
1、遞歸介紹
C允許一個函數條用其本身,這種調用過程被稱爲遞歸。
遞歸與循環比較:
採用遞歸函數解決問題的思路叫遞歸
採用循環解決同樣問題的思路叫遞推
遞歸一般可以代替循環語句使用。有些情況下使用循環語句比較好,而有些時候使用遞歸更有效。遞歸方法雖然使用程序結構優美,但其執行效率卻沒有循環語句高。一般來說,選擇循環更好一些。首先,因爲每次遞歸調用都擁有自己的變量集合,所以就需要佔用較多的內存;每次遞歸調用需要把心的變量集合存儲在堆棧中。其次,由於進行每次函數調用需要花費一定的時間,所以遞歸的執行速度較慢。
//示例一
#include <stdio.h>
void show (int);
int main (void)
{
show (1);
return 0;
}
void show (int n)
{
printf ("Level %d: n location %p", n, &n);
if (n < 4)
show (n + 1);
printf ("Level %d: n location %p", n, &n);
}
執行結果如下:
Level 1: n location 0xbf8bf680
Level 2: n location 0xbf8bf660
Level 3: n location 0xbf8bf640
Level 4: n location 0xbf8bf620
Level 4: n location 0xbf8bf620
Level 3: n location 0xbf8bf640
Level 2: n location 0xbf8bf660
Level 1: n location 0xbf8bf680
//示例二
#include <stdio.h>
void print (int num)
{
if (num == 1)
{
printf ("1");
return ;
}
printf ("%d", num);
print (num -1);
}
int main (void)
{
print (10);
return 0;
}
輸出結果:
10
9
8
7
6
5
4
3
2
1
2、遞歸的基本原理
第一:每一級的函數調用都有自己的變量。
第二:每一次函數調用都會有一次返回。
第三:遞歸函數中,位於遞歸調用前的語句和各級被調用函數具有相同的執行順序。
第四:遞歸函數中,位於遞歸調用後的語句的執行順序和各個被調用函數的順序相反。
第五:雖然每一級遞歸都有自己的變量,但是函數代碼並不會得到複製。
最後:遞歸函數中必須包含可以終止遞歸調用的語句。
3、遞歸的優缺點
其優點在於爲某些變成問題提供了最簡單的解決方法,而缺點是一些遞歸算法會很快耗盡計算機的內存資源。同時,使用遞歸的程序難於閱讀和維護。
五、指針函數和函數指針 可以加下小編的羣466572167,可以交流學習,也有資料下載
指針函數:返回指向char的指針的函數,例如:char * fump ( );
函數指針:指向返回類型char的函數指針,例如:char (* frump) ( );
這裏需要明白一個符號之間優先級的問題,"( )"的優先級比"*"要高。
再考慮一個,char (* flump[3]) ( );
由3個指針組成的數組,每個指針指向返回類型爲 char 的函數。