C函數介紹
函數是將代碼結構化為子程序的方法,我們可以:
- 為其命名
- 在需要的時候調用它們
從你的第一個程序開始,一個“Hello, World!”,你就立即使用了C函數:
#include <stdio.h>
int main(void) {
printf("Hello, World!");
}
main()
函數是非常重要的函數,因為它是C程序的入口點。
下面是另一個函數:
void doSomething(int value) {
printf("%u", value);
}
函數有4個重要方面:
- 它們有一個名字,所以我們可以在以後調用它們
- 它們指定了一個返回值
- 它們可以有參數
- 它們有一個用大括號包裹的函數體
函數體是在調用函數時執行的一組指令。
如果函數沒有返回值,你可以在函數名前使用關鍵字void
。否則,你需要指定函數的返回值類型(int
表示整數,float
表示浮點數,const char *
表示字符串等)。
你不能從一個函數中返回多個值。
函數可以有參數,它們是可選的。如果沒有參數,我們在括號內插入void
,像這樣:
void doSomething(void) {
/* ... */
}
在這種情況下,當我們調用該函數時,括號內不帶任何內容:
doSomething();
如果我們有一個參數,我們需要指定參數的類型和名稱,像這樣:
void doSomething(int value) {
/* ... */
}
當我們調用該函數時,我們將在括號內傳遞該參數,像這樣:
doSomething(3);
我們可以有多個參數,如果有多個參數,我們在聲明和調用時使用逗號分隔:
void doSomething(int value1, int value2) {
/* ... */
}
doSomething(3, 4);
參數是按值傳遞的。這意味著如果你修改了value1
的值,它的值只會在本地中被修改,並且在函數外部,它被傳入調用中的值不會改變。
如果你將一個指針作為參數傳遞,你可以修改該變量的值,因為你現在可以直接使用其內存地址訪問它。
你不能為參數定義默認值。C++可以這樣做(因此Arduino語言程序可以這樣做),但C不能。
確保在調用函數之前先定義它,否則編譯器將引發警告和錯誤:
➜ ~ gcc hello.c -o hello; ./hello
hello.c:13:3: warning: implicit declaration of
function 'doSomething' is invalid in C99
[-Wimplicit-function-declaration]
doSomething(3, 4);
^
hello.c:17:6: error: conflicting types for
'doSomething'
void doSomething(int value1, char value2) {
^
hello.c:13:3: note: previous implicit declaration
is here
doSomething(3, 4);
^
1 warning and 1 error generated.
你得到的警告是關於排序,這一點我已經提到過。
錯誤是與另一個相關的東西。由於C在調用之前不“看到”函數聲明,它必須做出假設。它假定函數返回int
。然而,該函數返回void
,因此出現了錯誤。
如果你將函數定義更改為:
int doSomething(int value1, int value2) {
printf("%d %d\n", value1, value2);
return 1;
}
你只會得到警告,而不是錯誤:
➜ ~ gcc hello.c -o hello; ./hello
hello.c:14:3: warning: implicit declaration of
function 'doSomething' is invalid in C99
[-Wimplicit-function-declaration]
doSomething(3, 4);
^
1 warning generated.
無論如何,確保在使用函數之前聲明它。要么把函數往上移,要么在頭文件中添加函數原型。
在函數內部,你可以聲明變量。
void doSomething(int value) {
int doubleValue = value * 2;
}
變量在函數調用時被創建,並在函數結束時被銷毀,它對外部不可見。
在函數內部,你可以調用函數自身。這稱為遞迴,它提供了特殊的機會。