C語言函數

一、函數的概念

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)
作者:碼農有道

如果文章有錯別字,或者內容有錯誤,或其他的建議和意見,請您留言指正,非常感謝!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章