ARTICLE DETAIL

资讯详情

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

C语言入门,函数知识讲解

C语言入门,函数知识讲解 目录1. 函数的概念2. 库函数1. 标准库和头文件2. 库函数的使用3. 自定义函数1. 函数的语法形式2. 函数举例4. 形参和实参1. 实参2. 形参3. 实参和形参的关系5. return语句6. 数组做函数参数7. 嵌套调用和链式访问1. 嵌套调用2. 链式访问8. 函数的声明与定义1. 单个文件2. 多个文件3. static和extern前言函数是 C 语言程序的核心组成单元。在前面的学习中我们已经知道C 语言程序由一条条语句构成而今天我们将换一个角度来认识它——C 语言程序其实是由一个个函数组成的。无论是数学中的一次函数y kx b还是程序中完成特定任务的代码块函数都扮演着输入 → 处理 → 输出的角色。掌握函数是学好 C 语言的关键一步。本文将从函数的概念出发系统讲解库函数与自定义函数、形参与实参、return 语句、数组传参、嵌套调用与链式访问以及函数的声明与定义等核心知识点并通过大量可运行的代码示例帮助你真正理解并掌握 C 语言函数的使用。1.函数的概念在数学中我们或多或少了解并学习过函数方面的知识如一次函数:ykxb…等。我们在之前讲到C语言程序由一条条语句构成但是在今天我们也许会换个说法C语言程序由一个个函数组成。在C语言中函数也叫“子程序”函数是完成某项特定任务的一小段代码。一般我们会见到两类函数·库函数·自定义函数2.库函数1.标准库和头文件C语言国际标准ANSI C规定了一些常用的函数的标准叫做“标准库”但是没有给出具体的库函数。不同编译器的厂商根据标准库对这些常见函数进行实现这些函数就叫“库函数”。库函数的实现在一定程度上提升了效率程序员只需学会就可以使用。库函数根据功能的划分在使用时要包含对应的头文件。库函数相关的头文件链接: link2.库函数的使用库函数学习和查看的工具比如C/C官方linkcplusplus.comlink这里我们用sqrt函数举例代码演示#includestdio.h#includemath.h//sqrt的头文件intmain(){doublersqrt(16);//参数可以是double类型也可以是整型整型会被转化为double类型printf(%.1lf,r);return0;}我们通过阅读库函数文档得出文档的一般格式·函数原型·函数功能介绍·参数和返回类型说明·代码举例·代码输出·相关知识链接3.自定义函数库函数实现的是一些常见的函数自定义函数可以给代码带来更多的创造性。1.函数的语法形式ret_typefun_name(形式参数){}ret_type函数的返回类型fun_name函数名括号内放形式参数{ }括起来的是函数体形象化理解把函数想成一个小型加工厂工厂通过输送来的原材料进行加工生产出产品函数通过传过来的参数进行计算得到计算结果。ret_type用来表示函数计算结果的数据类型有时候不需要返回值类型写为void。fun_name函数的名字根据函数的功能进行取名尽量要有意义方便调用。参数作为原材料有时不需要参数类型写为void 有参数时写清楚参数的类型和名字参数个数。{ }括起来的是函数体函数体就是计算的过程。2.函数举例举例写一个加法函数完成2个整型变量的加法操作。#includestdio.h//函数的定义intAdd(intx,inty){returnxy;//返回的是表达式先计算表达式得出结果返回的是计算结果}intmain(){inta0;intb0;scanf(%d %d,a,b);//输入整型变量的值intrAdd(a,b);//函数的调用//返回计算结果赋值给rprintf(%d\n,r);return0;}4.形参和实参函数的参数可以分成2类实参和形参1.实参根据上面代码分析在调用Add函数时把a和b的值分别传递到函数参数x和ya和b作为真实传递给函数的参数叫做“实参”。2.形参x和y叫做“形式参数”简称“形参”。为什么叫做形式参数Add函数定义完后若不去调用函数x和y只是在形式上存在的参数并不会向内存申请空间不会真实存在所以叫形式参数。只有当函数被调用为了存放实参传递过来的值才会向内存申请空间。3.实参和形参的关系实参传递给形参有一定的联系但是实参和形参各自具有独立的内存空间请看下面代码和调试演示#includestdio.h//函数的定义intAdd(intx,inty){returnxy;//返回的是表达式先计算表达式得出结果返回的是计算结果}intmain(){inta0;intb0;scanf(%d %d,a,b);//输入整型变量的值intrAdd(a,b);//函数的调用//返回计算结果赋值给rprintf(%d\n,r);return0;}我们根据以上可以得出·形参修改不会影响实参·形参是实参的一份临时拷贝·形参和实参的名字可以相同但仍然处于不同内存空间5.return语句函数使用时经常包含return语句这里是对使用return语句的一些注意事项进行说明。·return后面可以是一个数值也可以是一个表达式若是表达式则先计算表达式再返回表达式计算结果。·若函数返回类型为void则return后面可以什么都没有直接写成return;·return语句执行后函数彻底返回若return语句后面还有代码也不执行对第2、3点进行代码演示// //n是负数就直接返回// //n是大于20,就打印呵呵// //否则就打印哈哈#includestdio.hvoidtest(intn){if(n0)return;//这里是void类型return后面没有数值或表达式elseif(n20)printf(hehe\n);elseprintf(haha\n);printf(heihei\n);//输出heihei是一条单独的语句//若n0return语句直接返回不会打印heihei}intmain(){intn0;scanf(%d,n);test(n);return0;}调试·若return返回的值和函数返回类型不一样系统会将返回的值隐式转换为函数的返回类型代码演示#includestdio.hinttest(){return3.4;}intmain(){intntest();printf(%d,n);return0;}调试·函数中若存在if等分支语句要确保每种情况下都要有return返回,否则会出现编译错误代码演示#includestdio.hinttest(){intn0;scanf(%d,n);if(n0)return1;//逻辑漏洞未写明n0返回的情况导致编译出错}intmain(){test();return0;}调试·如果不写函数返回的类型编译器会默认函数的返回类型是int代码演示#includestdio.htest(){return3.4;}//建议写清楚函数的返回类型不需要返回值写voidintmain(){intntest();printf(%d,n);return0;}**·**函数写了返回类型但是函数内没有使用return返回值那么函数返回的值是未知的代码演示#includestdio.hinttest(){inta0;intb0;intcab;}intmain(){intntest();printf(%d\n,n);return0;}调试6.数组做函数参数在使用函数时难免会遇到把数组作为参数传递给函数在函数内部对函数进行操作。如写一个函数将一个整型数组的内容全部设置为-1在写一个打印数组的内容//写一个函数将一个整型数组的内容全部设置为 - 1在写一个打印数组的内容#includestdio.h//函数的定义voidset_arr(intbrr[],intsz)//这里只需要将数组内的全部元素设置为-1不需要返回值函数返回类型为void{inti0;for(i0;isz;i){brr[i]-1;//赋值}}voidprintf_arr(intbrr[],intsz){inti0;for(i0;isz;i){printf(%d ,brr[i]);}}intmain(){intarr[]{1,2,3,4,5,6,7,8,9,10};intszsizeof(arr)/sizeof(arr[0]);//计算数组内元素个数//遍历设置数组内每一个元素为-1//一维数组作为参数写法函数名(数组名,数组内元素个数);set_arr(arr,sz);//函数的调用//输出printf_arr(arr,sz);//与set_arr函数类似return0;}在这里我们需要知道数组传参的几个重点知识·函数的形式参数和实参个数要一样才能匹配的上·函数的实参是数组形式参数也要写成数组形式·形参如果是一维数组数组的大小可以省略不写·形参如果是二维数组数组的行可以省略不写列必须写·数组传参形参是不会创建新的数组的·形参操作的数组与实参的数组是同一个数组我们再写一个二维数组作为参数的如用函数输出一个定义好的二维数组//二维数组传参,用函数将数组输出#includestdio.hvoidprintf_arr(intarr[][5],intr,intc){inti0;for(i0;i3;i){intj0;for(j0;j5;j){printf(%d ,arr[i][j]);}printf(\n);}}intmain(){intarr[3][5]{{1,2,3},{2,3,4},{3,4,5,6,7}};//输出//二维数组作为参数写法函数名(数组名,行,列);printf_arr(arr,3,5);return0;}7.嵌套调用和链式访问1.嵌套调用嵌套调用就是函数之间的相互调用。假设让我们去计算某年某月有几天我们可以设计两个函数is_leap_year()判断该年是否为闰年再对特殊的2月进行讨论get_days_of_month()在调用is_leap_year()进行闰年判断将对应天数输出#includestdio.h#includestdbool.h//函数的定义boolis_leap_year(inty){if(((y%40)(y%100!0))||(y%4000))returntrue;elsereturnfalse;}intget_days_of_month(inty,intm){intdays[]{0,31,28,31,30,31,30,31,31,30,31,30,31};//0 1 2 3// 输出对应月份的下标可以得到对应天数但是下标没对齐我们在第一个补0将下标向后移动1位intddays[m];if(is_leap_year(y)(m2))//闰年 2月多1天{d1;}returnd;}intmain(){intyear0;intmonth0;scanf(%d %d,year,month);intdayget_days_of_month(year,month);//函数的调用printf(%d\n,day);//输出天数return0;}在上面的代码中main函数调用了scanf、printf、get_days_of_month()get_days_of_month()则调用了is_leap_year()2.链式访问链式访问就是将一个函数的返回值作为另一个函数的参数像链条一样将函数串起来比如#includestdio.h#includestring.hintmain(){//size_t len strlen(abcdef);//strlen返回值的类型是size_t类型////size_t属于无符号整型一般优先用%zu打印但也有人用%zd打印//printf(%zu, len);printf(%zu,strlen(abcdef));//strlen的返回值作为printf的参数形成一个链式访问return0;}再看一个有趣的代码#includestdio.hintmain(){printf(%d,printf(%d,printf(%d,43)));//printf的返回值是打印在屏幕上的字符个数return0;}//按照从左往右的顺序//第三个printf函数首先输出43读取到2个字符返回2//第二个printf函数输出2读取到1个字符返回1//第一个printf函数输出1读取到1个字符返回1//输出43218.函数的声明与定义1.单个文件一般我们在使用函数时直接把函数写出来就可以了比如我们写输入一个年份可以去判断该年是否为闰年#includestdio.h//函数的定义intis_leap_year(inty){if(((y%40)(y%100!0))||(y%4000))return1;elsereturn0;}intmain(){intyear0;scanf(%d,year);is_leap_year(year);//函数的调用if(is_leap_year(year)1)printf(闰年);elseprintf(平年);return0;}当函数的定义在函数的调用前时函数可以正常运行得到结果如果将函数的定义放到函数的调用之后会发生什么呢#includestdio.hintmain(){intyear0;scanf(%d,year);is_leap_year(year);//函数的调用if(is_leap_year(year)1)printf(闰年);elseprintf(平年);return0;}//函数的定义intis_leap_year(inty){if(((y%40)(y%100!0))||(y%4000))return1;elsereturn0;}此时编译显示的结果为原来C语言程序是从第一行往下进行编译的当编译到函数的调用时发现前面没有编译到这个函数的定义啊我不知道该怎么办程序就发出了警告。怎么解决这个问题呢就是在函数的调用之前声明一下is_leap_year这个函数向程序说明我定义了这个函数位置在函数的调用之后修改后#includestdio.h//函数的声明//告诉编译器有一个函数函数的名字、参数、返回类型分别是什么//参数只保留类型可以省略名字intis_leap_year(int);//函数的声明 函数的定义的第一句照搬到前面再加一个;就行intmain(){intyear0;scanf(%d,year);is_leap_year(year);//函数的调用if(is_leap_year(year)1)printf(闰年);elseprintf(平年);return0;}//函数的定义intis_leap_year(inty){if(((y%40)(y%100!0))||(y%4000))return1;elsereturn0;}·函数的调用一定要满足先声明后调用函数的定义是一种特殊的说明可以放在函数的调用之前2.多个文件一般在企业中代码可能比较多我们就会根据程序的功能将代码拆分到多个文件中。一般情况下函数的声明、类型的声明放在.h的头文件中函数的实现放在.c的源文件中·因为是自己创建的头文件要用 括起来不使用 ·add.c和add.h名字可以不一样这里是为了好区分·不要头文件程序也可以使用3.static和externstatic和extern作为C语言的关键字。static表示静态的意思可以用来修饰局部变量、全局变量、函数。extern是用来声明外部符号的,外部符号可以是全局变量、函数等。在讲解static和extern之前我们要先了解一下作用域和生命周期作用域一段代码可以发挥作用的范围1.局部变量作用域变量所在的局部范围2.全局变量作用域整个工程项目生命周期变量的创建申请内存到变量的销毁收回内存的一个时间段1.局部变量的生命周期进入作用域变量创建生命周期开始到变量出作用域时生命周期结束2.全局变量的生命周期整个程序的生命周期进入main函数的开始到结束1.static修饰局部变量先看两段代码代码1#includestdio.hvoidtest(){intn1;//因为n是局部变量每次进入函数n都要重新创建到销毁n;printf(%d ,n);}intmain(){inti0;for(i0;i5;i){test();}}//输出2 2 2 2 2代码2#includestdio.hvoidtest(){staticintn1;//编译时创建好n,每一次进入函数不用重新创建nn保留上一次的值继续参与运算n;//printf(%d ,n);}intmain(){inti0;for(i0;i5;i){test();}}//输出2 3 4 5 6代码1的test函数中的局部变量i是每次进入test函数先创建变量生命周期开始并赋值为0然后n再打印出函数的时候变量生命周期将要结束释放内存。代码2中我们从输出结果来看n的值有累加的效果其实test函数中的n创建好后出函数的时候是不会销毁的重新进入函数也就不会重新创建变量直接上次累积的数值继续计算。结论static改变了局部变量的生命周期生命周期的改变本质是改变了变量的存储类型本来一个局部变量存储在内存的栈区但是被static修饰之后存储到了内存的静态区与全局变量存储在同一个内存区自然而然被修饰过的局部变量就跟全局变量拥有了一样的生命周期只有程序被销毁内存才会被回收但是作用域不变。使用建议未来一个变量出了函数后我们还想保留值等下次进入函数继续使用就可以使用static修饰。2.static修饰全局变量结论一个全局变量被static修饰后只能在本源文件内使用不能被其他源文件使用。原因由于全局变量具有外部链接属性外部的文件想要使用只需要适当的声明就可以了但是当全局变量被static修饰之后全局变量从外部链接属性变成了内部链接属性就只能自己所在的内部源文件内使用即使声明了也不能被外部源文件使用。使用建议如果一个全局变量只想在所在的源文件内部使用不想被其他文件发现就可以使用static修饰。3.static修饰函数static修饰函数的原理跟修饰全局变量的一样都是从外部链接变成内部链接的。使用建议一个函数只想在所在的源文件内部使用不想被其他源文件使用就可以使用static修饰。总结本文系统梳理了 C 语言函数的核心知识点让我们回顾一下重点内容1. 函数的概念函数是完成某项特定任务的一小段代码C 语言程序由一个个函数组成。函数分为库函数和自定义函数两大类。2. 库函数库函数由编译器厂商根据 ANSI C 标准实现使用前需要包含对应的头文件。学会查阅官方文档是使用库函数的关键。3. 自定义函数自定义函数由ret_type fun_name(形式参数) { }构成返回类型、函数名、参数和函数体各司其职为代码带来更多创造性。4. 形参和实参实参是真实传递给函数的参数形参是实参的一份临时拷贝。形参和实参各自具有独立的内存空间形参的修改不会影响实参。5. return 语句return 后面可以是数值或表达式void 类型函数可以写return;return 执行后函数立即返回返回值类型不一致时会隐式转换分支语句要确保每种情况都有 return不写返回类型默认是 int写了返回类型但没 return返回值是未知的。6. 数组做函数参数数组传参时形参不会创建新数组形参操作的数组与实参是同一个数组。一维数组形参可省略大小二维数组形参可省略行但必须写列。7. 嵌套调用和链式访问嵌套调用是函数之间的相互调用链式访问是将一个函数的返回值作为另一个函数的参数像链条一样串起来。8. 函数的声明与定义函数的调用必须满足先声明后调用企业中常将声明放在.h头文件、实现放在.c源文件static修饰局部变量改变其生命周期修饰全局变量和函数则将其从外部链接变为内部链接extern用于声明外部符号。掌握了这些知识点你就具备了编写模块化、可复用 C 程序的基础能力。建议动手运行文中的每一个示例在实践中加深理解。
返回列表