news 2026/7/26 8:25:18

数组作为函数参数为什么要多传一个长度_Day10

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数组作为函数参数为什么要多传一个长度_Day10

数组作为函数参数为什么要多传一个长度?——Day10 学习记录,从数组传参到日历打印

学完函数之后,我开始用函数封装各种功能。但很快就遇到了问题——把数组传给函数后,用sizeof计算长度,结果根本不是数组的大小。

后来才知道,这是 C 语言最经典的数组退化为指针规则。

今天就把数组传参、字符串操作函数、作用域和存储类别串起来讲一遍,最后用一个打印日历的例子收尾。

① 数组传参的本质:为什么不是传整个数组?

很多新手(包括我)第一次写数组传参时,都会这么想:“把整个数组传进去不就行了?”

但 C 语言偏不。它不传整个数组,只传首元素的地址

原因:数组可能很大。如果每次传参都复制整个数组,性能开销太大了。C 语言的设计哲学是"相信程序员,不做多余的事"。

所以数组名做参数时,编译器把它解释为指针——准确地说,是退化为首元素地址。既然函数拿到的只是一个地址,它当然不知道数组有多长。这就是为什么必须额外传一个长度参数。

与之对比:字符数组(字符串)不需要传长度,因为字符串自带\0结束标志。函数只需一直读到\0就知道到哪结束了。

一句总结:数组传参退化为指针,函数拿不到数组大小,所以要么额外传长度,要么靠 ‘\0’ 结尾。

② 一维数组传参怎么写?

错误示例

voidprintArray(inta[]){intlen=sizeof(a)/sizeof(a[0]);// 错!sizeof(a) 是指针大小}

这个坑我踩过不止一次。sizeof(a)在函数里返回的是指针的字节数(8 或 4),不是数组的大小。

正确写法

voidprintArray(inta[],intlen){for(inti=0;i<len;i++)printf("%d ",a[i]);}printArray(a,10);// 调用时传入长度

字符数组就不一样了:

voidprintStr(chars[]){for(inti=0;s[i]!='\0';i++)printf("%c",s[i]);}printStr("hello");// 不需要传长度

一句总结:整型数组传参要加长度参数,字符数组靠 ‘\0’ 结尾。

③ 二维数组传参:为什么列数不能省略?

错误示例

voidoutputArray(inta[][],introw);// 编译错误

为什么错

二维数组本质上是"数组的数组"。访问a[i][j]时,编译器需要计算偏移量:

地址 = 首地址 + (i * 列数 + j) * 元素大小

列数不指定,这个公式就没法算。行数可以省略,因为编译器不需要行数就能算出地址。

正确写法

voidoutputArray(inta[][4],introw){for(inti=0;i<row;i++){for(intj=0;j<4;j++)printf("%d ",a[i][j]);printf("\n");}}

二维字符数组同理:char str[][10],列数必须写,行数可以省略。

一句总结:二维数组传参必须指定列数,因为编译器需要列数来定位元素。

④ 字符串操作函数(自己动手实现)

标准库提供了string.h,但自己实现一遍能更深刻理解原理。

Strlen:计算字符串长度

遇到\0就停,统计\0之前的字符数。

intmyStrlen(constchars[]){inti=0;while(s[i]!='\0')i++;returni;}

Strcpy:复制字符串

注意要把\0也复制过去。

voidmyStrcpy(chardest[],constcharsrc[]){inti=0;while(src[i]!='\0'){dest[i]=src[i];i++;}dest[i]='\0';// 别忘了复制结束符}

Strcat:拼接字符串

先找到 dest 的末尾(\0的位置),再从那里开始复制 src。

voidmyStrcat(chardest[],constcharsrc[]){inti=0,j=0;while(dest[i]!='\0')i++;// 找到 dest 末尾while(src[j]!='\0'){dest[i]=src[j];i++;j++;}dest[i]='\0';}

Strcmp:比较字符串

逐个字符比较 ASCII 码,找到第一个不同的字符就返回差值。

intmyStrcmp(constchars1[],constchars2[]){inti=0;while(s1[i]!='\0'&&s2[i]!='\0'){if(s1[i]!=s2[i])returns1[i]-s2[i];i++;}returns1[i]-s2[i];}

一句总结:四个操作函数的本质都是遍历 ‘\0’ 结尾的字符数组。

⑤ 作用域与存储类别

作用域讲的是"在哪能用",生命周期讲的是"啥时候销毁"。

局部变量 vs 全局变量

intg=10;// 全局变量:所有函数都能用voidfunc(){inta=5;// 局部变量:只能在 func 里用}

就近原则:如果局部和全局同名,局部优先。

存储类别关键字

关键字存储位置生命周期作用域说明
auto块内块内默认,局部变量
static全局区程序始终块内只初始化一次
register寄存器(建议)块内块内编译器可忽略
extern外部存储程序始终全局声明外部变量

static最常用——它让局部变量在函数多次调用之间保持值不变,但其他函数又访问不到。

一句总结:作用域决定可见性,static 让局部变量拥有全局生命周期但保持局部访问限制。

⑥ 综合实战:打印日历

今天最后一个实战:打印 2026 年 7 月的日历。

拆成四个函数,每个只做一件事:

#include<stdio.h>intisLeapYear(intyear){return(year%4==0&&year%100!=0)||(year%400==0);}intgetMonthDays(intyear,intmonth){intdays[]={31,28,31,30,31,30,31,31,30,31,30,31};if(month==2&&isLeapYear(year))return29;returndays[month-1];}intgetAllDays(intyear,intmonth){inttotal=0;for(inty=1;y<year;y++)total+=isLeapYear(y)?366:365;for(intm=1;m<month;m++)total+=getMonthDays(year,m);returntotal;}voidprintCal(intyear,intmonth){intdays=getMonthDays(year,month);intweekday=(getAllDays(year,month)+1)%7;// 2026.1.1 是周四printf(" %d 年 %d 月\n",year,month);printf("日 一 二 三 四 五 六\n");for(inti=0;i<weekday;i++)printf(" ");for(intd=1;d<=days;d++){printf("%2d ",d);if((weekday+d)%7==0)printf("\n");}printf("\n");}intmain(){printCal(2026,7);return0;}

核心思路:从公元 1 年 1 月 1 日到目标年月的 1 日,算出总天数,总天数 % 7 得到星期几,再按格式输出。

每日一练

运行上面的代码,验证 2026 年 7 月的日历是否正确。你可以把年份和月份改成其他值测试。

今日避坑指南

原因怎么避
函数内 sizeof(a) 不是数组大小数组名退化为指针额外传 len
二维数组列数省略导致编译错误编译器无法计算偏移形参写int a[][N]
Strcpy 忘记复制 \0目标数组不是合法字符串循环结束后加dest[i]='\0'
Strcat 目标空间不够拼接后越界确保 dest 容量足够
全局变量滥用函数间耦合高、难调试能用传参就别用全局
忘记闰年判断2 月天数算错isLeapYear 单独封装

今日学习感悟

今天最有价值的收获,是理解了 C 语言"数组传参退化为指针"这条规则。以前写sizeof(a)在函数里算出指针大小时,我只觉得莫名其妙。今天搞明白了背后的原因——性能优先的设计哲学,一切都有道理。

另一个收获是日历打印这个实战。把一个看起来复杂的问题拆成四个小函数,每个函数独立测试,最后组合起来。这个拆分思路在嵌入式开发中特别重要:一个函数只做一件事,做好一件事。


Day10 学习记录,明天继续。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/26 8:20:03

C++流程控制:顺序、选择与循环结构详解与实战

1. 从“顺序”到“选择”与“循环”&#xff1a;理解程序运行的脉络当我们谈论编程&#xff0c;尤其是像C这样的结构化编程语言时&#xff0c;流程结构是构建程序逻辑的骨架。你可以把它想象成烹饪一道菜的步骤清单。最开始&#xff0c;你可能是按部就班地执行&#xff1a;洗菜…

作者头像 李华
网站建设 2026/7/26 8:19:56

ETP-R1系统:视觉-语言导航在连续空间中的强化学习与拓扑规划

1. 项目背景与核心挑战视觉-语言导航&#xff08;VLN&#xff09;是近年来智能体研究领域的热点方向&#xff0c;它要求智能体根据自然语言指令在真实3D环境中完成导航任务。传统方法在离散网格环境中表现尚可&#xff0c;但面对连续空间时常常遭遇三大瓶颈&#xff1a;动作空间…

作者头像 李华
网站建设 2026/7/26 8:16:38

AI驱动Java单元测试自动化:Diffblue Cover实战从30%到80%覆盖率提升

1. 项目概述&#xff1a;当覆盖率成为团队瓶颈接手一个遗留的Java项目&#xff0c;打开单元测试覆盖率报告&#xff0c;看到那个刺眼的30%&#xff0c;相信很多开发者都经历过这种瞬间血压升高的感觉。代码库庞大&#xff0c;逻辑复杂&#xff0c;历史债务沉重&#xff0c;手动…

作者头像 李华
网站建设 2026/7/26 8:16:22

发改136号文新能源入市全流程拆解|全网独家复现交易收益仿真模型 机制竞价/市场注册/现货结算全链路落地、风光储协同优化收益稳定性

目录 摘要 一、政策核心背景与行业变革痛点 二、136号文核心政策框架与存量增量差异化规则 2.1 存量新能源项目规则(2025年6月1日前投产) 2.2 增量新能源项目规则(2025年6月1日后投产) 2.3 核心收益模式革新 三、新能源标准化入市全流程(四步闭环) 3.1 第一步:机…

作者头像 李华
网站建设 2026/7/26 8:16:17

AI论文降重工具实测与合规化处理方案

1. 项目背景与核心挑战去年帮导师审稿时发现一个有趣现象&#xff1a;同一课题组连续三篇论文在查重系统里AI率从90%骤降到10%以下&#xff0c;但行文风格却出奇一致。这个发现促使我系统测试了2026年市面上主流的4款AI降重工具&#xff0c;结合传统人工修改方法&#xff0c;总…

作者头像 李华
网站建设 2026/7/26 8:15:55

Code Review最佳实践

Code Review最佳实践 在软件开发中&#xff0c;Code Review&#xff08;代码审查&#xff09;是保证代码质量、提升团队协作能力、减少Bug的重要环节。作为一名全栈工程师&#xff0c;我深知代码审查的重要性&#xff0c;它不仅是对代码的检查&#xff0c;更是知识的传递和团队…

作者头像 李华