实参传递是传送值给函数上对应的参数,值会复制一份给参数,来源变量与接受的参数各有一个内存地址,互不相干,例如:
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
数组。