函數是C語言的重要組成部分,通過函數我們可以將複雜邏輯進行封轉,縮減程序員在編碼和維護中的關注點數量,提高代碼質量,方便對代碼進行維護。
函數聲明用來說明我們的代碼中包含一個什麼樣的函數,函數聲明可以放在頭文件(.h)或者源文件(.c)中,函數聲明的格式如下:
返回值類型 函數名(參數表);
函數定義是函數的實現,函數定義的格式如下:
返回值類型 函數名(參數表) {
C語言語句
}
函數聲明不是必須的,如果省略了函數聲明,我們必須將函數定義按照調用順序進行組織,比如下面的僞代碼:
void foo1() {
........
// foo3(); //編譯錯誤,找不到函數
// foo2(); //編譯錯誤,找不到函數
}
void foo3() {
foo1(); //正確,foo1()在foo3()前面定義
........
// foo2(); //編譯錯誤,找不到函數
}
void foo2() {
foo3(); //正確,foo3在foo2前面定義
.......
}
所以,一般把開放給外部的函數聲明放在.h文件中,然後在使用這些函數的.c文件中通過#include導入,
演示一個簡單的函數,function.h中進行聲明
#ifndef FUNCTION_H
#define FUNCTION_H
int add(int a, int b);
#endif
function.c中定義
#include "function.h"
int add(int a, int b) {
return a + b;
}
該函數實現兩個整型數的相加,通過兩個參數a和b,將計算結果a+b通過return語句返回。main.c中使用該函數
#include <stdio.h>
#include "function.h"
int main(void) {
int a = 10;
int b = 20;
printf("a + b = %d\n", add(a, b));
}
函數的輸出爲
a + b = 30
下面我們着重討論函數的參數,在上面函數定義中,參數列表中的a和b被稱爲形參,他們是函數作用域內的局部變量,如果函數外部還有變量a和b(如main中的變量a和b),形參將會隱藏外部的同名變量。
函數的參數按值傳遞,什麼叫按值傳遞呢?如果參數是基本類型的變量,如int, float等,則將變量值進行傳遞,我們對形參的改變不會改變外部變量的值,如果傳入的參數是指針,則傳入的參數是指針變量的值,也就是一個內存地址,這樣,我們基於形參的指針變量,是可以改變內存地址指向的內存中的數值的,看下面改版的add函數,在function.h中加入一個新的函數聲明
int add(int a, int b);
void add_use_pointer(int a, int b, int *result);
add_use_pointer函數不返回值,而是通過一個指針變量result返回值。function.c中的實現如下
void add_use_pointer(int a, int b, int *result) {
*result = a + b;
}
不再調用return語句,而是將a+b的值賦值給了一個指針變量所指向的內存。main.c中的調用
int result = 0;
add_use_pointer(a, b, &result);
printf("a + b = %d\n", result);
輸出結果爲
a + b = 30
可以看到,main中定義的result,初始化爲0,但是執行函數add_use_pointer之後,值通過指針被設置爲了30。使用指針返回值,可以讓我們的函數輕鬆返回多個值,不過,使用指針返回多個值,不應該返回過多的值,否則會加長參數列表,這是非常不好的代碼形式。如果採用return,希望返回多個值,則需要將返回值綁爲一個結構體(後面的文章會介紹),而使用結構體,是C語言實現面向對象的一種手段。
介紹完函數,我們補充看一個問題,C語言中{}之間的部分被稱之爲代碼塊,在代碼塊中定義的變量,其作用域就在該代碼塊中,也就是說,在代碼塊之外無法訪問該變量,另外,如果沒有使用動態內存分配,局部變量在離開代碼塊時,佔用的內存會被釋放,生存期僅限於代碼塊。如果一個變量不屬於任何函數,那麼該變量就是一個全局變量,如果在任何代碼塊中都沒有同名變量,則我們可以在代碼的所有位置訪問到該變量,生存期爲整個程序的運行時間。看下面的例子,我們在function.c中定義了一個全局變量
int global_int = 100;
在function.h中,我們使用extern關鍵字導出該全局變量
extern int global_int;
main.c中我們包含了頭文件function.h,所以也就導出了全局變量,我們可以直接使用
printf("global_int = %d\n", global_int);
main.c中我們包含了頭文件function.h,所以也就導出了全局變量,我們可以直接使用
輸出爲
global_int = 100
C語言還提供了一個static關鍵字,用來定義靜態變量或靜態函數,對於靜態變量,可以分爲靜態全局變量和靜態局部變量。給一個簡單的靜態全局變量的例子
static int global_int_static = 100;
我們提到了作用域的問題,靜態全局變量和靜態函數作用域爲文件作用域,也就是說,變量或函數僅在本文件中有效。這裏就爲我們提供了一種封裝機制,如果我們想隱藏某個函數或全局變量不被外部訪問,我們可以在.c文件中將其定義靜態全局變量或靜態函數。但是,如果在.h文件中定義了靜態全局變量和靜態函數,則由於#include會將頭文件複製到引用點,實際上就將靜態全局變量和靜態函數所在的文件改變了,造成了可以在其他文件訪問的假象。
對於靜態局部變量,變量僅在第一次被使用時會被初始化,之後每次進入其作用域,都會保留之前在其作用域中的運行值,也就是說生存期被延長爲整個程序運行階段,但是作用域依然是局部的。
static是我們使用C語言做出面向對象程序的工具之一,static相關的內容比較抽象,希望大家多去了解一些。