引數傳遞是傳送值給函式上對應的參數,值會複製一份給參數,來源變數與接受的參數各有一個記憶體位址,互不相干,例如:
int main(void) {
int x = 10;
....
printf("%d\n", increment(x));
printf("%d\n", x);
return 0;
}
int increment(int n) {
n = n + 1;
return n;
}
在這個程式片段中,x
的值複製給 increment
函式的參數 n
,n
雖然作了遞增運算,但是對 x
的值並無影響,x
最後仍是顯示 10。
在傳值應用上,也可以將變數的位址值取出,傳遞位址值給指定的指標參數,只要使用 &
運算子就可以了。
int main(void) {
int x = 10;
....
printf("%d\n", increment(&x));
printf("%d\n", x);
return 0;
}
int increment(int *n) {
*n = *n + 1;
return *n;
}
在這個程式中,increment
的參數 n
是個指標,在呼叫 increment
函式時,使用取址運算 &
將 x
變數的位址值傳遞給指標 n
,而在函式中,使用取值運算 *
取得該位址的值,進行遞增動作之後再指定給該位址,因此程式最後透過 x
變數取得的值會是 11。
在函式上宣告指標參數之目的,是希望函式中可以有變動同一位址的值,如此一來,呼叫者可以保留函式中變動的結果。
運用的場景之一是,C 呼叫函式後只能傳回一個值,若在呼叫函式時,想取得兩個以上的運算結果,就可以使用指標參數。
如果函式不傳回值,使用 void
表示不傳回任何數值;若函式傳回型態不為 void
,在函式中一定要使用 return
傳回數值,否則編譯器會回報錯誤。
函式也可以傳回位址,這意味著呼叫者可以對該位址取值或變更,例如下面的程式中,在函式中動態配置連續的 int
空間,並傳回空間的位址:
#include <stdio.h>
#include <stdlib.h>
int* ints(int, int);
int main(void) {
int size = 0;
int init = 0;
printf("陣列大小:");
scanf("%d", &size);
printf("元素初值:");
scanf("%d", &init);
int *arr = ints(size, init);
for(int i = 0; i < size; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
return 0;
}
int* ints(int size, int init) {
int *arr = malloc(size * sizeof(int));
for(int i = 0; i < size; i++) {
arr[i] = init;
}
return arr;
}
執行結果:
陣列大小:5
元素初值:3
arr[0] = 3
arr[1] = 3
arr[2] = 3
arr[3] = 3
arr[4] = 3
由於使用動態配置的方式,被配置的空間在函式執行過後,不會自動清除,可以直接傳回位址給呼叫者,如果是底下範例,陣列空間會在函式執行完後清除,編譯器會提出警訊,傳回指標值也就沒有意義,也會造成存取錯誤:
#include <stdio.h>
#include <stdlib.h>
int* ints(int, int);
int main(void) {
int size = 0;
int init = 0;
printf("陣列大小:");
scanf("%d", &size);
printf("元素初值:");
scanf("%d", &init);
int *arr = ints(size, init);
for(int i = 0; i < size; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
return 0;
}
int* ints(int size, int init) {
int arr[size];
for(int i = 0; i < size; i++) {
arr[i] = init;
}
return arr; // warning: function returns address of local variable
}
如果要傳遞陣列給函式,方式之一是明確宣告陣列型態,這必須包含陣列長度:
#include <stdio.h>
#include <stdlib.h>
void printInts(int arr[5]) {
for(int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
}
int main(void) {
int arr[] = {1, 2, 3, 4, 5};
printInts(arr);
return 0;
}
當然,這就寫死了長度資訊,另一個方式是指定長度:
#include <stdio.h>
#include <stdlib.h>
void printInts(int len, int arr[len]) {
for(int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
}
int main(void) {
int arr[] = {1, 2, 3, 4, 5};
printInts(5, arr);
return 0;
}
這是因為 C99 以後,多數編譯器支援可變長度的陣列型態(variable length array type),然而記得 len
參數必須先出現,編譯器看到後才能用於後續參數。
由於 C11 將可變長度的陣列型態標示為非必要功能,如果編譯器真的不支援,可以使用傳遞陣列位址的方式:
#include <stdio.h>
#include <stdlib.h>
void printInts(int len, int *arr) {
for(int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
}
int main(void) {
int arr[] = {1, 2, 3, 4, 5};
printInts(5, arr);
return 0;
}
在傳遞陣列時,C99 以後可以直接傳遞常量,例如:
#include <stdio.h>
#include <stdlib.h>
void printInts(int len, int *arr) {
for(int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
}
int main(void) {
printInts(5, (int[]) {1, 2, 3, 4, 5});
return 0;
}
(int[]) {1, 2, 3, 4, 5}
的 int[]
轉型是必要的,編譯器需要這項資訊知道這是個 int
陣列。