文章目錄
一、函數的概念
1、函數的概念
每個C程序都至少有一個函數,即main主函數 ,如果程序的任務比較簡單,全部的代碼都寫在main函數中,但是,在實際開發中,程序的任務往往比較複雜,如果全部的代碼都寫在main函數中,main函數體將非常龐大臃腫,代碼重複。
我們可以把程序的任務分工到不同的子函數中,main更關心業務邏輯和處理流程,需要執行某任務的時候,調用子函數就可以了。
2、函數的分類
爲了方使理解,我們把函數分爲庫函數和自定義函數(子函數)。
庫函數是C語言提供的,實現了某些基本的功能,例如scanf、printf,在程序中可以直接使用。
自定義函數是程序員爲了完成某項任務而編寫的函數,目的是爲了實現某項的功能或讓主程序更簡潔。自定義函數在使用之前,必須先聲明和定義。
二、自定義函數的聲明
自定義函數的聲明包括了返回值、函數名和參數列表。有些程序員把函數聲明說成是函數原型,只是用詞不同,意思是一樣的。
C語言中的聲明函數的語法如下:
return_type function_name( parameter list );
1)返回值的數據類型return_type:函數執行完任務後的返回值,可以是int、char、double或其它自定義的數據類型。如果函數只執行任務而不返回值,return_type用關鍵字 void表示,如下:
void function_name( parameter list );
2)函數名function_name:函數名是標識符,命名規則與變量相同。
3)參數列表parameter list:當函數被調用時,調用者需要向函數傳遞參數。參數列表包括參數的數據類型和書寫順序。參數列表是可選的,也就是說,函數可以沒有參數,如下:
return_type function_name();
函數聲明示例:
// 判斷超女身材函數的聲明
// 函數名:checksc
// 參數:height,身高,單位cm。
// 返回值:0-不合格;1-嬌小;2-標準;3-高挑
int checksc(int height);
聲明瞭一個函數,返回值是int類型,函數名是checksc,函數只有一個參數int height,注意,函數的聲明語句最後的分號不能少。
如果把自定義函數比喻成工具,函數的聲明就是這個工具的設計圖紙。
三、自定義函數的定義
自定義函數是工具,如果想讓這個工具能被使用,光有設計圖紙還不行,還要把工具製造出來,函數的定義就是這個工具的實際主體,是爲了實現函數的功能而編寫的代碼。
C語言中的函數定義的語法如下:
return_type function_name( parameter list ) // 注意,不要在函數定義的最後加分號。
{
// 實現函數功能的代碼
}
函數定義的return_type、function_name和parameter list必須與函數聲明一致。
函數主體包含爲了完成任務需要執行語句的集合,放在花括號內。
示例
// 判斷超女身材函數的定義
int checksc(int height)
{
if ( (height>=160) && (height<165) ) return 1; // 嬌小
if ( (height>=165) && (height<175) ) return 2; // 標準
if ( (height>=175) && (height<180) ) return 3; // 高挑
return 0; // 不合格
}
四、自定義函數代碼的組織
1、非通用能功的函數
如果自定義函數只在調用者程序中使用,可以在調用者程序中聲明和定義,聲明一般爲調用者程序的上部,定義一般在調用者程序的下部,這並不是C語言的規定,而是爲了讓程序更方便閱讀,程序員約定的寫法。
示例(book45.c)
/*
* 程序名:book45.c,此程序演示自定義函數在主程序中聲明和定義
* 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <string.h>
// 判斷超女身材函數的聲明
// 函數名:checksc
// 參數:height,身高,單位cm。
// 返回值:0-不合格;1-嬌小;2-標準;3-高挑
int checksc(int height);
int main()
{
int ii,jj;
printf("請輸入超女的身高(單位:釐米):");
scanf("%d",&ii);
jj=checksc(ii); // 調用checksc函數判斷身材
if (jj==0) printf("不合格。\n");
if (jj==1) printf("嬌小。\n");
if (jj==2) printf("標準。\n");
if (jj==3) printf("高挑。\n");
}
// 判斷超女身材函數的定義
int checksc(int height)
{
if ( (height>=160) && (height<165) ) return 1; // 嬌小
if ( (height>=165) && (height<175) ) return 2; // 標準
if ( (height>=175) && (height<180) ) return 3; // 高挑
return 0; // 不合格
}
在book45.c中,判斷超女身材的函數(checksc)不是公共的功能,只在選秀程序使用,所以它的聲明和定義都寫在book45.c中。
2、通用能功的函數
如果自定義函數是一個通用的功能模塊,可以在公共的頭文件中聲明,在公共的程序文件中定義。
如果某程序需要調用公共的函數,在調用者程序中用#include指令包含公共的頭文件,編譯的時候把調用者程序和公共的程序文件一起編譯。
在以下的示例中,函數min和max是公共的函數,在_public.h頭文件中聲明,在_public.c程序文件中定義,在book46.c程序中被調用。
1)公共的頭文件(_public.h)
/*
* 程序名:_public.h,公共功能函數聲明的頭文件
* 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 聲明min函數,用於比較兩個整數的大小,取小者
int min(const int ii1,const int ii2);
// 聲明max函數,用於比較兩個整數的大小,取大者
int max(const int ii1,const int ii2);
2)公共的程序文件(_public.c)
/*
* 程序名:_public.c,公共功能函數聲明的程序文件
* 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include "_public.h" // 包含自定義函數聲明的頭文件
// 用於比較兩個整數的大小,函數返回較小者
int min(const int ii1,const int ii2) // min函數定義
{
if (ii1<ii2) return ii1;
return ii2;
}
// 用於比較兩個整數的大小,函數返回較大者
int max(const int ii1,const int ii2) // max函數定義
{
if (ii1>ii2) return ii1;
return ii2;
}
3)調用者程序文件(book46.c)
/*
* 程序名:book46.c,此程序演示了在主程序的外部文件中聲明和定義函數
* 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include "_public.h" // 把_public.h頭文件包含進來
int main()
{
int xx,yy,imin,imax;
xx=50; yy=80;
imin=min(xx,yy);
imax=max(xx,yy);
printf("imin=%d,imax=%d\n",imin,imax);
}
4)編譯運行
3、注意事項
1)在book46.c的第5行#include "_public.h"
,這裏包含頭文件是用雙引號,不是尖括號<>,這兩者的差別如下:
#include <>
用於包含系統提供的頭文件,編譯的時候,gcc在系統的頭文件目錄中尋找頭文件。
#include ""
用於包含程序員自定義的頭文件,編譯的時候,gcc先在當前目錄中尋找頭文件,如果找不到,再到系統的頭文件目錄中尋找。
2)編譯程序的時候,要把調用者程序和公共程序文件一起編譯,否則編譯器會報錯。
3)C語言對公共函數的頭文件(_public.h)和程序文件(_public.c)的命名沒有規定,由程序員自己命名,爲了增加程序的可讀性,儘可能採用一些有意義的文件名。
五、庫函數
C語言提供了很多標準函數(C standard library),簡稱庫函數,調用這些函數可以完成一些基本的功能,例如printf、scanf、memset、strcpy等。C語言的庫函數有幾百個,常用的不超過30%,在以後的章節中我將詳細介紹常用庫函數的應用場景和使用方法。
C語言標準庫函數的聲明的頭文件存放在/usr/include目錄中,如下:
<asset.h> <ctype.h> <errno.h> <float.h> <limits.h>
<locale.h> <math.h> <setjmp.h> <signal.h> <stdarg.h>
<stddef.h> <stdlib.h> <stdio.h> <string.h> <time.h>
C語言庫函數的定義的文件是/usr/lib/gcc/x86_64-redhat-linux/4.4.4/libgcc.a
(不同編譯器的版本目錄名有所不同),這是一個打包好的庫文件,把程序文件打包成庫文件的方法以後再詳細介紹。
我們用gcc編譯程序的時候並沒有把libgcc.a包含進來,那是因爲gcc編譯器缺省會包含它,但是,程序員自定義函數的程序文件就沒有這種待遇,需要手工的包含進來與程序一起編譯。
六、需要包含哪些頭文件
我們在使用庫函數的時候,如果不包含它的頭文件,編譯時會出現警告或錯誤,如下:
那麼,在我們編寫的程序中,應該包含哪些頭文件呢?有兩種方法,一是上百度上查資料,二是使用Linux系統提供的幫助,以strcpy函數爲例,在命令行下輸入man strcpy回車,如下:
man顯示了函數的聲明的頭文件(第5行),函數的參數、使用方法和返回值。
注意了,如果程序中調用了庫函數但沒有包含它頭文件,編譯的時候不一定是警告,也可能是報錯,函數無法識別等,這個要看編譯器,不同C語言的編譯器的處理方式不一樣。
七、調用函數的方法
庫函數是系統提供的工具,自定義函數是程序員自己補充的工具,對使用者來說都一樣,沒有區別。
在C語言中,函數的調用非常靈活,可以獨佔一行語句,也可以當成常量賦值給變量,也可以當成函數的參數。
如果函數的返回值是void,表示該函數的調用一定會成功,一般用單行書寫,獨佔一條語句。
如果函數的返回值不是void,可以不關心它的返回值,也可以當成常量用於任何表達式中。
示例(book47.c)
/*
* 程序名:book47.c,此程序演示了函數的靈活調用。
* 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include "_public.h" // 把_public.h頭文件包含進來
int main()
{
int i=max(30,45);
max(30,20); // 沒有接收返回值,max的活白乾了。
55; // 這是什麼鬼?
i=max(min(45,38),50);
i=min(max(min(45,38),50),25);
printf("30和50相比,更大的值是%d。\n",max(30,50));
}
大家可能對這個代碼不理解,一句話:如果函數的返回值是整數,就可以把函數當成整數來用,如果返回值是小數,就可以把函數當成小數來用,……。
八、函數調用的過程
在程序中調用子函數時,程序的流程進入子函數的代碼中,當子函數返回時(或到達函數的結束括號時),程序的流程回到調用者程序。
示例(book48.c)
/*
* 程序名:book48.c,此程序演示了函數的調用的過程
* 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <string.h>
// 聲明func函數,無返回值無參數
void func();
int main()
{
printf("即將調用func函數。\n");
func();
printf("調用func函數已完成。\n");
return 0;
}
void func()
{
int ii=0;
for (ii=0;ii<5;ii++)
{
sleep(1); // sleep是C語言的庫函數,睡眠
printf("已經過去了%d秒。\n",ii+1);
}
}
運行效果
九、函數參數的傳遞
函數的參數可以理解爲函數內部的變量,參數傳遞就是調用者程序把變量(或常量)的值複製一份給函數的參數,簡單說來就是複製的過程。一旦完成數據的複製,調用者程序的變量和函數的參數就再也沒有瓜葛了,所以,在函數調用的過程中,函數內部變量的值發生改變並不會影響調用者程序的變量的值。
我們在調用函數的時候,不關心函數參數的名稱,函數參數的名稱是函數自己的事情,是函數的內部變量,只在函數內部使用,與調用者無關。
示例(book49.c)
/*
* 程序名:book49.c,此程序演示了函數參數的傳遞
* 作者:C語言技術網(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
void funcld(int b); // 聲明函數funcld,無返回值
int main()
{
int a=10;
printf("調用funcld前a的值是%d.\n",a);
funcld(a);
printf("調用funcld後a的值是%d.\n",a);
}
void funcld(int b)
{
printf("賦值前b的值是%d.\n",b);
b=20;
printf("賦值後b的值是%d.\n",b);
}
運行效果
關於函數的參數,在很多教程中有很多說法,如“形參”、“實參”、“值傳遞”、“地址傳遞”等,這些說法把簡單的概念複雜化了,大家不必理會。
十、const約束
const 是 constant 的縮寫,意思是“恆定不變的”,它是定義只讀變量的關鍵字。用 const 定義只讀變量的方法很簡單,就在定義變量時前面加 const 即可,如:
const double pi = 3.1415926;
用 const 定義的變量的值是不允許改變的,不允許給它重新賦值,即使是賦相同的值也不可以。所以說它定義的是隻讀變量。這也就意味着必須在定義的時候就給它賦初值,如果程序中試圖改變它的值,編譯的時候就會報錯。
在變量前加const約束,主要用於定義函數的參數,表示該參數在函數中是隻讀取,不允許改變,如果函數中試圖改變它的值,編譯的時候就會報錯。
例如:
編譯如下:
爲什麼要在函數的參數前加const?有兩個目的:1)防止程序員犯錯,如果程序員犯了錯誤,編譯器就能發現;2)增加了源代碼的可讀性。在實際開發中,函數的參數不加const不會有任何問題,但是,程序員一般都會爲只讀變量加上const約束,這是一個好的習慣。
十一、應用經驗
1、如何尋找函數
在我的課程中,會介紹常用庫函數的使用方法,但也有漏掉的。查資料能力是程序員的基本技能,它可以提升您解決問題的能力。
例如要查找C語言複製字符串功能的函數,在百度上輸入“C語言複製字符串”搜索就可以了,您想查什麼就輸入什麼。
然後,打開多幾個網頁看看,基本上能找到答案。
注意一個問題,網上的內容太多太雜亂,大部分文章是初學者發的博客,重點關注百度百科的文章。
2、測試函數的功能
以strcpy函數爲例,函數的聲明如下。
char *strcpy(char *dest, const char *src);
函數功能:字符串複製。
函數名:strcpy。
dest參數:目標字符串。
src參數:待複製的內容,有const約束。
返回值:返回dest,這是提高函數可用性的常用的處理方法。
用代碼測試一下:
char strname[50];
strcpy(strname,"西施");
printf("=%s=\n",strname);
printf("=%s=\n",strcpy(strname,"西施"));
然後,看看輸出的結果是不是“=西施=”,用=號夾住輸出的內容是爲了看清楚有沒有其它的字符。
十二、課後作業
1)編寫示例程序,自定義一個根據超女的胸圍判斷身材的函數,判斷標準由您定,類似book45.c。
2)C語言的庫函數肯定是不夠用的,程序員必須要有自己的函數庫,從本章節就開始準備,創建您自己的函數庫的頭文件和程序文件,頭文件和程序文件的命名由您定,先放兩個函數進去,聲明如下,然後寫示例程序調用它們。
// 聲明max函數,取兩個數值的較大者
int max(int ii1,int ii2);
// 聲明min函數,取兩個數值的較小者
int min(int ii1,int ii2);
這兩個自定義函數的功能雖然簡單,但是,千里之行,始於足下,慢慢積累。
3)本章節的內容非常重要,建議大家看多幾遍視頻,用程序測試每個知識點,充分的理解它。
十三、版權聲明
C語言技術網原創文章,轉載請說明文章的來源、作者和原文的鏈接。
來源:C語言技術網(www.freecplus.net)
作者:碼農有道
如果文章有錯別字,或者內容有錯誤,或其他的建議和意見,請您留言指正,非常感謝!!!