ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C语言函数与数组传参入门:我两天踩过的7个坑(return、void、arr[i])

C语言函数与数组传参入门:我两天踩过的7个坑(return、void、arr[i]) 第一次写函数时我以为最难的是判断大小、闰年和月份天数。真正把我拦下来的却是一些更小、更基础的东西形参为什么还要写类型函数名后面少一对括号会发生什么数组传进函数后为什么还得把长度一起送进去。这篇只记录我这两天实际写错、编译、修改并重新测试过的7个问题。它不是函数语法大全而是一份仍带着报错痕迹的初学者复盘。上一篇学习记录里我写过一句话代码能运行不等于我已经会编程。学到函数以后这句话又具体了一层。以前的问题通常藏在某条语句里函数把程序切成了几个部分错误也随之分散有时出在函数头有时出在调用位置有时表面上没有语法错误程序却根本没有执行我以为自己调用的那个函数。数组加入以后情况更热闹了。arr、arr[i]、arr[i]只差几个字符含义却一路分叉。我开始意识到函数不是给代码套一个名字数组传参也不是把数组名塞进括号。它们都要求我回答同一个问题数据从哪里来经过谁处理最后到哪里去1. 写了返回类型不等于写完整了函数头我最先完成的是“返回两个整数中的较大值”。判断本身不难我第一次却把函数头写成了intmaxTwo(x,y)我当时只盯着最前面的int以为“整数函数”已经交代清楚。编译器看到的却是另一回事maxTwo确实返回int可是x和y分别是什么类型代码没有说。实际编译时出现了这样的信息error: type of x defaults to int error: type of y defaults to int正确写法是intmaxTwo(intx,inty){if(xy)returnx;elsereturny;}现在我会把函数头拆成三部分看int 返回值类型 maxTwo 函数名 (int x, int y) 形参列表每个形参都要有类型这处错误很小却把我从数学式的阅读习惯里拽了出来。C语言不接受“你应该知道我的意思”。类型没有写信息就没有交代完。2. 我以为自己在调用函数其实什么也没调用修好函数头以后我又在main里写下intmaxTwo(x,y);我脑子里的意思是“把x和y交给maxTwo”。但这行代码并没有保存计算结果甚至不像一次正常的函数调用。真正需要的是intmaxmaxTwo(x,y);两边承担的角色完全不同max 用来接收结果的变量 maxTwo(x, y) 函数调用函数调用最不能缺的是括号和实参。只写函数名或者把一段看似正式的声明塞进main都不会替我完成计算。修正后输入8 15得到15输入20 5得到20。我又补了一组相等值7 7返回7。这组测试看似多余其实是在确认else分支不仅处理“小于”也处理“等于”。3.int和void决定了函数是否需要返回结果接下来我写了一个判断正数、负数和零的函数。最初的函数头是intlast(inti)函数体里只有printf没有return。编译器随即提醒warning: control reaches end of non-void function这条警告把问题说得很直白我声明它会返回一个整数函数走到末尾时却什么也没交出来。这个函数的任务只是打印因此更合适的返回类型是voidvoidprintSign(intnumber){if(number0)printf(positive\n);elseif(number0)printf(negative\n);elseprintf(zero\n);}我还顺手把原来含义模糊的last改成了printSign。函数名当然可以自定义但名字如果能直接说出职责下一次回来看代码时就少猜一步。int和void不只是函数名前面的格式。它们在描述函数的约定调用者是否应该得到一个结果。需要把最大值、天数或判断结果带回去我用int只负责打印、输入或修改数组我用void。这条区分一旦清楚后面的函数设计突然顺了不少。4.return一执行函数就结束了在maxTwo的早期版本中我还在if...else后面留了一句return0;完整结构相当于if(xy)returnx;elsereturny;return0;最后一句永远到不了。当x y成立函数从return x离开条件不成立函数从return y离开。所有路径都已经结束后面的代码失去了执行机会。我以前把return理解成“给出答案”。现在多了一层理解它同时也是出口。程序执行到这里当前函数立即收束控制权回到调用它的位置。5. 少一对括号闰年判断就没有真正发生闰年函数本身很快写通了intisLeapYear(intyear){if((year%40year%100!0)||year%4000)return1;elsereturn0;}真正的问题出现在“求某年某月有多少天”时。我最初写成if(month2isLeapYear)return29;看起来只差一个(year)。实际上这一对括号决定了函数有没有被调用。编译器给出的警告是warning: the address of isLeapYear will always evaluate as true只写isLeapYear时这里出现的是函数本身所对应的地址不是“这一年是否为闰年”的判断结果。只要month 2成立条件就可能被当成真。2023年的二月也会被错判成29天。正确写法是if(month2isLeapYear(year))return29;随后补上普通月份的返回intgetDaysOfMonth(intyear,intmonth){intdays[]{0,31,28,31,30,31,30,31,31,30,31,30,31};if(month1||month12)return0;if(month2isLeapYear(year))return29;returndays[month];}这里第一次出现了真正的函数嵌套main └─ getDaysOfMonth(year, month) └─ isLeapYear(year)我测试了四组容易拉开差异的数据输入返回结果2024 2292023 2282023 4302023 130表示月份非法写完这道题以后函数不再只是几段并排摆放的代码。它们开始互相协作了。6. 数组传进函数数组和长度要一起交代进入数组传参时我先写出了voidinputArr(intarr,intsize)问题在于int arr只表示一个普通整数而我传入的是整个数组。函数如果要逐个访问元素形参应该写成数组形式voidinputArr(intarr[],intsize)数组传进去还不够。函数要遍历到哪里也必须明确所以长度size要一起传入inputArr(arr,size);printArr(arr,size);这和我之前想象的“函数会自己知道数组有多长”不同。数组名告诉函数要处理哪一组数据size告诉它边界在哪里。两者缺一不可。在main中数组仍是一个完整数组因此可以计算元素个数intsizesizeof(arr)/sizeof(arr[0]);但到了函数里我不再重新使用这条公式猜长度而是使用明确传入的size。7.arr[i]负责写入arr[i]负责读取最后一组错误密集地挤在几行代码里。输入数组时我曾经写scanf(%d,arr);打印数组时又写过printf(%d,arr[]);前者没有指出要写入哪个元素后者不但缺少下标还把地址交给了需要整数值的%d。正确关系是scanf(%d,arr[i]);/* 写入第i个元素需要地址 */printf(%d ,arr[i]);/* 读取第i个元素直接使用值 */我还犯过一个更朴素的错误。inputArr的循环变量叫i到了printArr里我明明定义的是a打印时仍然写了arr[i]。编译器直接报error: i undeclared函数有自己的局部范围。前一个函数里的i不会跟着我走进下一个函数。最后我把两个函数统一整理成voidinputArr(intarr[],intsize){inti0;for(i0;isize;i){scanf(%d,arr[i]);}}voidprintArr(intarr[],intsize){inti0;for(i0;isize;i){printf(%d ,arr[i]);}printf(\n);}一个负责写入一个负责读取。动作很简单数据的流向却必须写得精确。把输入和打印合成一个可运行程序下面是这两天最后跑通的数组版本。整理文章时我又补上了元素个数检查避免在数量小于等于0时声明无效的变长数组。#includestdio.hvoidinputArr(intarr[],intsize){inti0;for(i0;isize;i){scanf(%d,arr[i]);}}voidprintArr(intarr[],intsize){inti0;for(i0;isize;i){printf(%d ,arr[i]);}printf(\n);}intmain(void){intcount0;printf(请输入元素个数);if(scanf(%d,count)!1||count0){printf(元素个数必须是正整数\n);return1;}intarr[count];intsizesizeof(arr)/sizeof(arr[0]);printf(请输入%d个整数\n,size);inputArr(arr,size);printf(数组内容\n);printArr(arr,size);return0;}测试输入5 3 6 9 12 15实际输出请输入元素个数请输入5个整数 数组内容 3 6 9 12 15一张表留下这7次修改我写过的代码问题修改方向int maxTwo(x, y)形参缺少类型int maxTwo(int x, int y)int maxTwo(x, y);没有正确调用并接收结果int max maxTwo(x, y);int last(int i)但没有return返回类型与函数行为不一致改为void在完整if...else后继续return 0前面所有路径已经返回删除不可达代码month 2 isLeapYear只有函数名没有调用isLeapYear(year)int arr只能表示一个整数数组形参写成int arr[]并传长度arr、arr[]、arr[i]变量混乱地址、元素和作用域混在一起输入用arr[i]输出用arr[i]这张表比背一段函数定义更适合我。下次再看到类似报错我至少知道第一眼该检查哪里。两天以后我会了什么又还不会什么我现在能够定义简单的int函数和void函数能把实参传给形参也能接住返回值可以写最大值、正负判断和闰年判断还完成了getDaysOfMonth调用isLeapYear的嵌套结构。数组方面我已经实际写过一维数组传参知道函数通常还需要一个长度参数并能在函数中输入、修改和打印元素。但这些能力仍然很窄。函数声明放在main前、定义放在main后我只看过还需要自己重新写二维数组参数、递归、多文件工程、static和extern没有形成独立实践。指针尚未系统学习我也不会用指针术语假装解释数组传参的底层细节。相比第一周我的变化不是突然会写更长的代码。更准确地说我开始追踪代码里的交接关系谁接收参数谁返回结果谁修改数组哪个变量只在当前函数内有效。函数把程序拆开了也迫使我把思路说得更清楚。下一步我准备在不看旧代码的情况下重写isLeapYear、getDaysOfMonth和数组输入输出再补一题“函数返回数组最大值及其下标”。如果只能照着现成代码敲一遍这一轮学习还不能算真正完成。你第一次写函数时最容易混淆的是返回值、函数调用还是数组参数如果遇到过“编译通过但函数根本没按预期执行”的情况也欢迎把那一行代码留下来。
返回列表