在掌握一维数组与函数的声明、调用之后,需要弄清数组如何作为实参传入函数:既可以传递单个元素,也可以传递整个数组或二维数组。本节说明对应的形参写法,并说明数组形参为何会退化为指针,以及为何通常需要同时传入元素个数。

传递单个数组元素

若函数只关心数组中的某一个下标,可以把该元素作为普通变量传入,此时按值传递:函数收到的是该元素的一份副本,在函数内修改形参不会影响调用方的数组元素(除非传入的是指针,那是另一章的内容)。

#include <stdio.h>

void display(int age1, int age2) {
    printf("%d\n", age1);
    printf("%d\n", age2);
}

int main(void) {
    int ageArray[] = {2, 8, 4, 12};

    display(ageArray[1], ageArray[2]);   /* 传入下标 1、2 的元素 */
    return 0;
}

输出:

8
4

传递整个一维数组

要把整段连续元素交给函数处理,调用时一般只写数组名(表示首元素地址),形参可写成 float age[] 或等价的 float *age。下列程序通过函数计算数组元素之和:

#include <stdio.h>

float calculateSum(float age[], int n);

int main(void) {
    float age[] = {23.4f, 55, 22.6f, 3, 40.5f, 18};
    float result = calculateSum(age, 6);
    printf("结果 = %.2f\n", result);
    return 0;
}

float calculateSum(float age[], int n) {
    float sum = 0.0f;
    for (int i = 0; i < n; ++i) {
        sum += age[i];
    }
    return sum;
}

输出:

结果 = 162.50

调用处写 calculateSum(age, 6):第一个实参是数组名,第二个实参是元素个数。定义处的 float age[] 表示接收指向 float 的指针;方括号内不写长度是允许的,因为函数内无法从形参本身可靠地得到数组总长度。

此处传递的是首元素地址,并不是把整个一维数组复制进函数;若在 calculateSum 内修改 age[i],调用方 main 中的 age 数组会同步改变。

传递二维数组

二维数组同样以数组名作为实参传入。形参必须标明列数(第二维长度),编译器才能按行计算下标偏移;行数可以写在第一维,也可以省略由另一参数传入。

#include <stdio.h>

void displayNumbers(int num[][2], int rows);

int main(void) {
    int num[2][2];
    printf("输入 4 个整数:\n");
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 2; ++j) {
            scanf("%d", &num[i][j]);
        }
    }
    displayNumbers(num, 2);
    return 0;
}

void displayNumbers(int num[][2], int rows) {
    printf("数组内容:\n");
    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < 2; ++j) {
            printf("%d\n", num[i][j]);
        }
    }
}

输出示例(输入 2、3、4、5 时):

输入 4 个整数:
2
3
4
5
数组内容:
2
3
4
5

若写成 int num[2][2] 作为形参,则只适用于固定为 2 行 2 列的情形;更通用的写法是 int num[][2] 并增加 rows 参数,与一维数组传入长度的方式一致。

二维数组名同样表示首地址,传递的不是矩阵副本;函数内若改写 num[i][j],main 里读入的 num 数组内容也会一并改变。

数组形参退化为指针

用数组名调用函数时,实参传递的是首元素地址,而不是把整个数组按字节复制进函数栈。因此在函数内部:

  • 通过下标或指针修改元素,会作用到调用方的原数组(双方共享同一段存储)。
  • 形参 float age[] 与 float *age 在类型上等价,均表示指向 float 的指针。
  • 对形参使用 sizeof(age) 得到的是指针类型的大小,而不是原数组占用的总字节数;这也是一维、二维数组作参数时通常要另传 n、rows 等原因。

三种常见写法可对照如下:

调用方式典型形参实际传递的内容
f(arr[i])int x单个元素的副本
f(arr)int arr[] / int *arr指向首元素的指针
f(mat)(二维)int mat[][COL]指向首行首元素的指针,列数 COL 须已知

小结:表中后两种写法传递的是首地址,并不是把整个数组按元素复制一份进函数;因此在函数内修改下标对应的元素时,调用方数组中的值会同步改变。仅第一种传递单个元素属于按值传递,函数内改动形参不会影响原数组。

C 语言数组作为函数参数求和示例运行
#include <stdio.h>

int sum(int arr[], int n) {
    int s = 0;
    for (int i = 0; i < n; ++i) {
        s += arr[i];
    }
    return s;
}

int main(void) {
    int a[] = {1, 2, 3, 4, 5};
    printf("%d\n", sum(a, 5));
    return 0;
}

C 语言中可以把数组(首地址)传入函数,但不能通过返回值安全地交回整个局部数组:若返回指向函数内局部数组的指针,函数结束后该存储可能失效。需要把结果写回调用方提供的缓冲区,或在堆上分配并由调用方负责释放。

本节说明了数组作实参时的写法与退化规则。下一章将系统介绍指针,进一步说明数组名与指针在表达式中的关系。