1. 引言
今天学习了 C 语言中「数组作为函数参数」这一重要知识点。数组作为函数参数是 C 语言编程中非常基础且高频使用的技巧,无论是处理一组数据、实现排序算法,还是进行矩阵运算,都离不开它。本文将系统梳理数组作为函数参数的几种方式、底层原理以及常见注意事项,帮助你把今天学到的内容巩固扎实。
2. 为什么需要把数组传给函数
在 C 语言中,函数是模块化编程的基本单位。当我们需要对一组数据进行统一处理时(比如求平均值、排序、查找),如果每次都在主函数里写一遍逻辑,代码会变得冗长且难以维护。把数组作为参数传给函数,就能把数据处理逻辑封装起来,实现代码复用。
例如,求一个数组所有元素的和,如果不用函数,每次都要写循环;而封装成函数后,只需调用一次即可。
3. 数组作为函数参数的两种基本写法
3.1 数组名作为参数(传数组首地址)
这是最常用的方式。函数形参写成数组形式,实参直接传数组名。
#include<stdio.h>// 形参写成数组形式intsumArray(intarr[],intn){intsum=0;for(inti=0;i<n;i++){sum+=arr[i];}returnsum;}intmain(){intscores[]={85,92,78,90,88};inttotal=sumArray(scores,5);printf("总分 = %d\n",total);return0;}3.2 数组名作为参数的另一种写法
数组名作为参数时,除了写成数组形式,也可以写成带长度的形式,两者在编译器看来是等价的:
intsumArray(intarr[],intn){intsum=0;for(inti=0;i<n;i++){sum+=arr[i];}returnsum;}这两种写法在编译器看来是等价的,int arr[]会被自动转换为int *arr。
rr`。
4. 数组作为函数参数的底层原理
理解底层原理是掌握这一知识点的关键。
4.1 传递的是地址,不是整个数组
当数组名作为实参传递时,C 语言不会复制整个数组,而是把数组的首地址(即第一个元素的地址)传给函数。这意味着:
- 函数内部对数组元素的修改,会直接影响主函数中的原数组;
- 传递效率高,无论数组多大,都只传一个地址。
4.2 为什么必须同时传数组长度
由于函数只拿到首地址,它并不知道数组到底有多长。因此,必须额外传递一个表示数组长度的参数,否则函数无法确定遍历的边界,容易越界访问。
// 错误示范:没有传长度,函数无法知道数组大小voidprintArray(intarr[]){// 无法确定循环次数!}5. 函数内修改数组会改变原数组
这是数组参数与普通变量参数最大的区别。普通变量按值传递,函数内修改不影响实参;而数组按地址传递,函数内修改会直接改掉原数组。
#include<stdio.h>voiddoubleArray(intarr[],intn){for(inti=0;i<n;i++){arr[i]*=2;// 直接修改原数组}}intmain(){intdata[]={1,2,3,4};doubleArray(data,4);// 此时 data 已变为 {2, 4, 6, 8}for(inti=0;i<4;i++){printf("%d ",data[i]);}return0;}6. 二维数组作为函数参数
二维数组作为参数时,形参必须指定第二维的大小(列数),第一维可以省略。
#include<stdio.h>// 必须写列数,行数可省略voidprintMatrix(intmat[][3],introws){for(inti=0;i<rows;i++){for(intj=0;j<3;j++){printf("%d ",mat[i][j]);}printf("\n");}}intmain(){intmatrix[2][3]={{1,2,3},{4,5,6}};printMatrix(matrix,2);return0;}为什么必须指定列数?因为编译器需要根据列数来计算mat[i][j]的实际内存地址偏移。
7. 常见错误与注意事项
7.1 忘记传数组长度
这是初学者最容易犯的错误。数组参数不会自带长度信息,务必显式传递。
7.2 在函数内用sizeof求数组大小
在函数内部对形参使用sizeof(arr),得到的是数组首元素的大小(通常是 4 字节),而不是整个数组的大小。这是很多人的误区:
voidbadFunc(intarr[]){intlen=sizeof(arr)/sizeof(arr[0]);// 错误!得到的是指针大小}7.3 越界访问
由于函数只拿到地址,如果传入的长度参数大于实际数组长度,就会发生越界访问,导致未定义行为。
7.4 用const保护只读数组
如果函数只需要读取数组而不修改,建议在形参前加const,防止误修改:
intsumArray(constintarr[],intn){// 只能读,不能写}8. 综合实战:数组排序
把今天学的知识综合运用一下,写一个用数组参数实现冒泡排序的完整程序:
#include<stdio.h>// 冒泡排序,直接修改原数组voidbubbleSort(intarr[],intn){for(inti=0;i<n-1;i++){for(intj=0;j<n-1-i;j++){if(arr[j]>arr[j+1]){inttemp=arr[j];arr[j]=arr[j+1];arr[j+1]=temp;}}}}voidprintArray(constintarr[],intn){for(inti=0;i<n;i++){printf("%d ",arr[i]);}printf("\n");}intmain(){intnums[]={64,34,25,12,22,11,90};intn=sizeof(nums)/sizeof(nums[0]);printf("排序前:");printArray(nums,n);bubbleSort(nums,n);printf("排序后:");printArray(nums,n);return0;}9. 总结
今天学习的「数组作为函数参数」可以总结为几个核心要点:
- 数组名作为参数传递的是首地址,函数内修改会改变原数组;
- 必须同时传递数组长度,否则函数无法确定边界;
int arr[]与int *arr在形参中完全等价;- 二维数组作参数必须指定列数;
- 函数内用
sizeof求数组大小是错误的; - 只读数组建议加
const保护。掌握了这些,你就已经理解了 C 语言中数组与函数结合的核心机制。接下来可以继续学习指针数组、函数指针等进阶内容,进一步加深对 C 语言内存模型的理解。