C語言學習四 — 函式與作用域規則

weixin_34337265發表於2018-12-11

函式

函式是一組一起執行一個任務的語句。每個 C 程式都至少有一個函式,即主函式 main() ,所有簡單的程式都可以定義其他額外的函式。

您可以把程式碼劃分到不同的函式中。如何劃分程式碼到不同的函式中是由您來決定的,但在邏輯上,劃分通常是根據每個函式執行一個特定的任務來進行的。

函式宣告告訴編譯器函式的名稱、返回型別和引數。函式定義提供了函式的實際主體。

C 標準庫提供了大量的程式可以呼叫的內建函式。例如,函式 strcat() 用來連線兩個字串,函式 memcpy() 用來複制記憶體到另一個位置。

函式還有很多叫法,比如方法、子例程或程式,等等

定義函式

C 語言中的函式定義的一般形式如下:

return_type function_name( parameter list )
{
   body of the function
}

在 C 語言中,函式由一個函式頭和一個函式主體組成。下面列出一個函式的所有組成部分:

  • 返回型別:一個函式可以返回一個值。return_type 是函式返回的值的資料型別。有些函式執行所需的操作而不返回值,在這種情況下,return_type 是關鍵字 void

  • 函式名稱:這是函式的實際名稱。函式名和引數列表一起構成了函式簽名。

  • 引數:引數就像是佔位符。當函式被呼叫時,您向引數傳遞一個值,這個值被稱為實際引數。引數列表包括函式引數的型別、順序、數量。引數是可選的,也就是說,函式可能不包含引數。

  • 函式主體:函式主體包含一組定義函式執行任務的語句。

    例項

    以下是 max() 函式的原始碼。該函式有兩個引數 num1 和 num2,會返回這兩個數中較大的那個數:

    int max_num(int num, int numT) {
        return num > numT ? num : numT;
    }
    
printf("max num is %d",max_num(5,9));
max num is 9
Process finished with exit code 0

函式宣告

函式宣告會告訴編譯器函式名稱及如何呼叫函式。函式的實際主體可以單獨定義。

函式宣告包括以下幾個部分:

return_type function_name( parameter list );

針對上面定義的函式 max(),以下是函式宣告:

int max(int num1, int num2);

在函式宣告中,引數的名稱並不重要,只有引數的型別是必需的,因此下面也是有效的宣告:

int max(int, int);

當您在一個原始檔中定義函式且在另一個檔案中呼叫函式時,函式宣告是必需的。在這種情況下,您應該在呼叫函式的檔案頂部宣告函式。

呼叫函式

建立 C 函式時,會定義函式做什麼,然後通過呼叫函式來完成已定義的任務。

當程式呼叫函式時,程式控制權會轉移給被呼叫的函式。被呼叫的函式執行已定義的任務,當函式的返回語句被執行時,或到達函式的結束括號時,會把程式控制權交還給主程式。

呼叫函式時,傳遞所需引數,如果函式返回一個值,則可以儲存返回值。例如:


#include <stdio.h>
 
/* 函式宣告 */
int max(int , int );
 
int main ()
{
   /* 區域性變數定義 */
   int a = 100;
   int b = 200;
  
 
   /* 呼叫函式來獲取最大值 */
   int  ret = max(a, b);
 
   printf( "Max value is : %d\n", ret );
 
   return 0;
}
 
/* 函式返回兩個數中較大的那個數 */
int max(int num, int numT) 
{
   return num > numT ? num : numT; 
}

函式引數

如果函式要使用引數,則必須宣告接受引數值的變數。這些變數稱為函式的形式引數

形式引數就像函式內的其他區域性變數,在進入函式時被建立,退出函式時被銷燬

當呼叫函式時,有兩種向函式傳遞引數的方式:

呼叫型別 描述
傳值呼叫 該方法把引數的實際值複製給函式的形式引數。在這種情況下,修改函式內的形式引數不會影響實際引數。
引用呼叫 通過指標傳遞方式,形參為指向實參地址的指標,當對形參的指向操作時,就相當於對實參本身進行的操作。

預設情況下,C 使用傳值呼叫來傳遞引數。一般來說,這意味著函式內的程式碼不能改變用於呼叫函式的實際引數。

傳值方式呼叫函式

向函式傳遞引數的傳值呼叫方法,把引數的實際值複製給函式的形式引數。在這種情況下,修改函式內的形式引數不會影響實際引數。

預設情況下,C 語言使用傳值呼叫方法來傳遞引數。一般來說,這意味著函式內的程式碼不會改變用於呼叫函式的實際引數。**函式 switch() 定義如下:

void switch_num(int a, int b) {
    int temp;
    temp = a;
    a = b;
    b = temp;
}
void print_switch() {
    int a = 100, b = 200;
    printf("before,a is : %d b is : %d\n", a, b);
    /* 呼叫函式來交換值 */
    switch_num(a, b);
    printf("after,a is : %d b is : %d", a, b);

}
before,a is : 100 b is : 200
after,a is : 100 b is : 200
Process finished with exit code 0

引用方式呼叫函式

通過引用傳遞方式,形參為指向實參地址的指標,當對形參的指向操作時,就相當於對實參本身進行的操作。

傳遞指標可以讓多個函式訪問指標所引用的物件,而不用把物件宣告為全域性可訪問。

void switch_num(int *a, int *b) {
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
void print_switch() {
    int a = 100, b = 200;
    printf("before,a is : %d b is : %d\n", a, b);
    /* 呼叫函式來交換值
    * &a 表示指向 a 的指標,即變數 a 的地址 
    * &b 表示指向 b 的指標,即變數 b 的地址 
    */
    switch_num(*a, *b);
    printf("after,a is : %d b is : %d", a, b);

}
before,a is : 100 b is : 200
after,a is : 200 b is : 100
Process finished with exit code 0

作用域規則

任何一種程式設計中,作用域是程式中定義的變數所存在的區域,超過該區域變數就不能被訪問。C 語言中有三個地方可以宣告變數:

  1. 在函式或塊內部的區域性變數
  2. 在所有函式外部的全域性變數
  3. 形式引數的函式引數定義中

讓我們來看看什麼是區域性變數、全域性變數和形式引數。

區域性變數

在某個函式或塊的內部宣告的變數稱為區域性變數。它們只能被該函式或該程式碼塊內部的語句使用。區域性變數在函式外部是不可知的。下面是使用區域性變數的例項。在這裡,所有的變數 a、b 和 c 是 main() 函式的區域性變數。


#include <stdio.h>
 
int main ()
{
  /* 區域性變數宣告 */
  int a, b;
  int c;
 
  /* 實際初始化 */
  a = 10;
  b = 20;
  c = a + b;
 
  printf ("value of a = %d, b = %d and c = %d\n", a, b, c);
 
  return 0;
}

全域性變數

全域性變數是定義在函式外部,通常是在程式的頂部。全域性變數在整個程式生命週期內都是有效的,在任意的函式內部能訪問全域性變數。

全域性變數可以被任何函式訪問。也就是說,全域性變數在宣告後整個程式中都是可用的。下面是使用全域性變數和區域性變數的例項:


#include <stdio.h>
 
/* 全域性變數宣告 */
int g;
 
int main ()
{
  /* 區域性變數宣告 */
  int a, b;
 
  /* 實際初始化 */
  a = 10;
  b = 20;
  g = a + b;
 
  printf ("value of a = %d, b = %d and g = %d\n", a, b, g);
 
  return 0;
}

在程式中,區域性變數和全域性變數的名稱可以相同,但是在函式內,區域性變數的值會覆蓋全域性變數的值,但不會改變全域性變數的值。下面是一個例項:


#include <stdio.h>
 
/* 全域性變數宣告 */
int g = 20;
 
int main ()
{
  /* 區域性變數宣告,此變數和全域性變數不是同一個 */
  int g = 10;
 
  printf ("value of g = %d\n",  g);//離誰近,就優先列印誰
 
  return 0;
}

形式引數

函式的引數,形式引數,被當作該函式內的區域性變數,它們不會覆蓋全域性變數。下面是一個例項:

#include <stdio.h>
/* 全域性變數宣告 */
int a = 20;
int main ()
{
/* 在主函式中的區域性變數宣告 */
int a = 10;
int b = 20;
int c = 0;
int sum(int, int);//未定義值,只是一個函式
printf ("value of a in main() = %d\n",  a);
c = sum( a, b);
printf ("value of c in main() = %d\n",  c);
return 0;
}
/* 新增兩個整數的函式 */
int sum(int a, int b)
{
printf ("value of a in sum() = %d\n",  a);
printf ("value of b in sum() = %d\n",  b);
return a + b;
}
value of a in main() = 10
value of a in sum() = 10
value of b in sum() = 20
value of c in main() = 30

C 的形參與實參

在 C 語言中,形參與實參雖然很簡單,但是,是大家比較容易混淆的一個點,這裡將為大家詳細的講解。

概念:從字面上理解,所謂形式引數即只只是宣告瞭一個作為引數的變數,並未直接進行賦值使用,而實際引數則相反。

如下例

#include <stdio.h>

int test(int,int); // 形參,只宣告

int main()
{
    int a,b;
    printf("%d",test(5,3)); // 實參,已賦值
}

int test(int a,int b) // 形參
{
    a=a+b;
    return a;
}

像上面在 test() 函式裡只宣告瞭最為引數的變數,而 main() 函式裡則對它賦了值。

初始化區域性變數和全域性變數

當區域性變數被定義時,系統不會對其初始化,您必須自行對其初始化。定義全域性變數時,系統會自動對其初始化,如下所示:

資料型別 初始化預設值
int 0
char '\0'
float 0
double 0
pointer NULL

正確地初始化變數是一個良好的程式設計習慣,否則有時候程式可能會產生意想不到的結果,因為未初始化的變數會導致一些在記憶體位置中已經可用的垃圾值。

程式碼已上傳github,點選此處即可到達

相關文章