12-函数、数组和指针
10.4 函数、数组和指针
假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为 marbles 的 int 类型数组。应该如何调用该函数?也许是下面这样:
total = sum(marbles); // 可能的函数调用
那么,该函数的原型是什么?记住,数组名是该数组首元素的地址,所以实际参数 marbles 是一个存储 int 类型值的地址,应把它赋给一个指针形式参数,即该形参是一个指向 int 的指针:
int sum(int * ar); // 对应的函数原型
sum() 从该参数获得了什么信息?它获得了该数组首元素的地址,知道要在该位置上找出一个整数。注意,该参数并未包含数组元素个数的信息。我们有两种方法让函数获得这一信息。第一种方法是,在函数代码中写上固定的数组大小:
int sum(int * ar) // 相应的函数定义
{
int i;
int total = 0;
for (i = 0; i < 10; i++) // 假设数组有10个元素
total += ar[i]; // ar[i] 与 *(ar + i) 相同
return total;
}
既然能使用指针表示数组名,也可以用数组名表示指针。另外,回忆一下, += 运算符把右侧运算对象加到左侧运算对象上。因此, total 是当前数组元素之和。
该函数定义有限制,只能计算10个 int 类型的元素。另一个比较灵活的方法是把数组大小作为第2个参数:
int sum(int * ar, int n) // 更通用的方法
{
int i;
int total = 0;
for (i = 0; i < n; i++) // 使用 n 个元素
total += ar[i]; // ar[i] 和 *(ar + i) 相同
return total;
}
这里,第1个形参告诉函数该数组的地址和数据类型,第2个形参告诉函数该数组中元素的个数。
关于函数的形参,还有一点要注意。只有在函数原型或函数定义头中,才可以用 int ar[] 代替 int * ar :
int sum (int ar[], int n);
int * ar 形式和 int ar[] 形式都表示 ar 是一个指向 int 的指针。但是, int ar[] 只能用于声明形式参数。第2种形式( int ar[] )提醒读者指针 ar 指向的不仅仅是一个 int 类型值,还是一个 int 类型数组的元素。
注意 声明数组形参 因为数组名是该数组首元素的地址,作为实际参数的数组名要求形式参数是一个与之匹配的指针。只有在这种情况下,
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);
{
//其他代码已省略
}
可以使用以上提到的任意一种函数原型和函数定义。
程序清单10.10演示了一个程序,使用 sum() 函数。该程序打印原始数组的大小和表示该数组的函数形参的大小(如果你的编译器不支持用转换说明 %zd 打印 sizeof 返回值,可以用 %u 或 %lu 来代替)。
程序清单10.10 sum_arr1.c 程序
// sum_arr1.c -- 数组元素之和
// 如果编译器不支持 %zd,用 %u 或 %lu 替换它
#include <stdio.h>
#define SIZE 10
int sum(int ar[], int n);
int main(void)
{
int marbles[SIZE] = { 20, 10, 5, 39, 4, 16, 19, 26, 31, 20 };
long answer;
answer = sum(marbles, SIZE);
printf("The total number of marbles is %ld.\n", answer);
printf("The size of marbles is %zd bytes.\n",
sizeof marbles);
return 0;
}
int sum(int ar[], int n) // 这个数组的大小是?
{
int i;
int total = 0;
for (i = 0; i < n; i++)
total += ar[i];
printf("The size of ar is %zd bytes.\n", sizeof ar);
return total;
}
该程序的输出如下:
The size of ar is 8 bytes.
The total number of marbles is 190.
The size of marbles is 40 bytes.
注意, marbles 的大小是40字节。这没问题,因为 marbles 内含10个 int 类型的值,每个值占4字节,所以整个 marbles 的大小是40字节。但是, ar 才8字节。这是因为 ar 并不是数组本身,它是一个指向 marbles 数组首元素的指针。我们的系统中用8字节存储地址,所以指针变量的大小是8字节(其他系统中地址的大小可能不是8字节)。简而言之,在程序清单10.10中, marbles 是一个数组, ar 是一个指向 marbles 数组首元素的指针,利用C中数组和指针的特殊关系,可以用数组表示法来表示指针 ar 。