假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为 marbles 的 int 类型数组。
应该如何声明以及调用函数?
记住,数组名是该数组首元素的地址,所以实际参数 marbles 是一个储存 int 类型值的地址,应把它赋给一个指针形式参数,即该形参是一个指向 int 的指针:
int sum(int * ar); // 对应的函数原型
int main() {
...
total = sum(marbles); // 函数调用
...
}
函数从该参数获得了什么信息?
它获得了该数组首元素的地址,知道要在该位置上找出一个整数。注意,该参数并未包含数组元素个数的信息。 我们有两种方法让函数获得这一信息。第一种方法是,在函数代码中写上固定的数组大小。另一个比较灵活的方法是把数组大小作为第 2 个参数。还可传递两个指针分别指向数组的起始位置和终止位置。
形参的注意事项
需要时刻注意形参是函数的临时变量。
#include<stdio.h>
int f(int* a, int* b);
int main()
{
int a[] = {2, 3, 4};
int b[] = {6, 3, 5};
f(a, b);
for(int i = 0; i < 3; i++)
printf("%d ", a[i]);
printf("\n");
for(int i = 0; i < 3; i++)
printf("%d ", *(b+i));
getchar();
return 0;
}
int f(int* a, int* b)
{
b = a;
return 0;
}
上面代码在 f() 中将 b 的地址赋值给 a,但是并不会改变 main 函数中的 a 和 b 数组。
函数声明和定义中的另一种形参表示
关于函数的形参,还有一点要注意。只有在函数原型或函数定义头中, 可以用 int ar[] 代替 int * ar,在其他地方是不可以的。
int sum1 (int ar[], int n); // 可以
int sum2 (int ar[], int n) { // 可以
...
}
int main() {
int ar[]; // 错误
}
int *ar 形式和 int ar[] 形式都表示 ar 是一个指向 int 的指针。但是,int ar[] 只能用于声明形式参数。第 2 种形式(int ar[])只是提醒读者指针 ar 指向的不仅仅一个 int 类型值,还是一个 int 类型数组的元素。
PS:还可以用 sizeof 运算符来证明 int ar[] 是一个指针而不是数组(怎么证明的,大家可以思考一下,答案放在最后)。
因为数组名是该数组首元素的地址,作为实际参数的数组名要求形式参数是一个与之匹配的指针。只有在这种情况下,C 才会把 int ar[] 和 int * ar 解释成一样。也就是说,ar 是指向 int 的指针。由于函数原型可以省略参数名, 所以下面 4 种原型都是等价的:
int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);
但是,在函数定义中不能省略参数名。下面两种形式的函数定义等价:
int sum(int *ar, int n)
{
// 其他代码已省略
}
int sum(int ar[], int n);
{
//其他代码已省略
}
思考题答案
#include<stdio.h>
int f(int *a, int b[]);
int main()
{
int a[] = {2, 3, 4};
int b[] = {6, 3, 5};
printf("main:%d\n", sizeof(b));
f(a, b);
getchar();
return 0;
}
int f(int *a, int b[])
{
printf("f:%d\n", sizeof(b));
return 0;
}
main 中的 sizeof 计算结果是:数组 b 的元素个数 * 数组 b 的元素类型占的内存大小,是会根据 b 数组的大小而改变的。例如,b 数组的元素个数是 5,则结果是 20。
f 中的 sizeof 计算结果是:指针占据的内存大小,是确定的。64 位系统的指针固定占 8 位,32 位系统的指针固定占 4 位。