函数文章目录函数一.函数的概念二.库函数一标准库和头文件二库函数的使用方法1.功能2.头文件包含3.实践4.库函数文档的一般格式三.自定义函数一语法形式二举例四.形参和实参一实参二形参三实参和形参的关系五.return 语句六.数组做函数参数七.嵌套调用和链式访问一嵌套调用二链式访问八.函数的声明和定义一单个文件二多个文件三static 和 extern1.static 修饰局部变量2.static 修饰全局变量3.static 修饰函数4.extern 声明外部符号一.函数的概念C语言引入函数function的概念有些翻译为子程序(更加准确⼀些)。C语言中的函数就是⼀个完成某项特定的任务的一小段代码。这段代码有特殊的写法和调用方法。C语言的程序其实是由无数个小的函数组合而成的也可以说一个大的计算任务可以分解成若干个较小的函数对应较小的任务完成。同时一个函数如果能完成某项特定任务的话这个函数也是可以复用的提升了开发软件的效率。在C语言中有两类函数库函数自定义函数二.库函数一标准库和头文件C语言标准中规定了C语言的各种语法规则C语言并不提供库函数C语言的国际标准ANSI C规定了一些常用的函数的标准被称为标准库不同的编译器厂商根据ANSI提供的C语言标准就给出了一系列函数的实现。这些函数就被称为库函数。前面内容中学到的printf、scanf都是库函数库函数也是函数不过这些函数已经是现成的只要学会就能直接使用了。有了库函数一些常见的功能就不需要程序员自己实现了提升了效率同时库函数的质量和执行效率上都更有保证。各种编译器的标准库中提供了一系列的库函数这些库函数根据功能的划分都在不同的头文件中进行了声明。库函数相关头文件C标准库标头二库函数的使用方法库函数的学习和查看工具例如cplusplus.com举例:sqrtdoublesqrt(doublex);//sqrt 是函数名//x 是函数的参数表⽰调⽤sqrt函数需要传递⼀个double类型的值//double 是返回值类型 - 表⽰函数计算的结果是double类型的值1.功能Compute square root 计算平方根Returns the square root of x.返回平方根2.头文件包含库函数是在标准库中对应的头文件中声明的所以库函数的使用务必包含对应的头文件。3.实践4.库函数文档的一般格式函数原型函数功能介绍参数和返回类型说明代码举例代码输出相关知识链接三.自定义函数自定义函数更重要也能给程序员写代码更多的创造性。一语法形式自定义函数和库函数是一样的形式如下ret_typefun_name(形式参数){}ret_type是函数返回类型返回类型可以是void表示什么都不返回。fun_name是函数名函数有了名字方便调用所以函数名尽量要根据函数的功能起的有意义。括号中放的是形式参数函数的参数也可以是void明确表示函数没有参数。如果有参数要交代清楚参数的类型和名字以及参数个数。{}括起来的是函数体是完成计算的过程。二举例1#includestdio.h2.intAdd(intx,inty)3.{4.returnxy;5.}6.intmain()7.{8.inta0;9.intb0;10.scanf(%d %d,a,b);11.intcAdd(a,b);12.printf(%d,c);13.return0;14.}四.形参和实参在函数使用的过程中把函数的参数分为实参和形参可以是值也可以是地址本质上都是值。一实参实际参数就是真实传递给函数的参数。例如上面代码第2~6行是Add函数的定义有了函数后在第11行调用Add函数时传递给函数的参数a和b称为实际参数简称实参。二形参上面代码中第2行定义函数的时候在函数名Add后的括号中写的x和y称为形式参数简称形参。实际上如果只是定义了Add函数而不去调用的话Add函数的参数x和y只是形式上存在的不会向内存申请空间不会真实存在的所以叫形式参数。形式参数只有在函数被调用的过程中为了存放实参传递过来的值才向内存申请空间这个过程就是形参的实例化。三实参和形参的关系实参是传递给形参的它们之间是有联系的但是形参和实参各自是独立的内存空间互不干扰所以参数名可以相同。这个现象是可以通过调试来观察。请看下面的代码和调试演示:在调试的时候可以观察到x和y确实得到了a和b的值但是x和y的地址和a和b的地址是不一样的所以可以理解为形参是实参的一份临时拷贝。五.return语句在函数的设计中函数中有时会出现return语句return语句使用的注意事项return后边可以是一个数值也可以是一个表达式如果是表达式则先执行表达式再返回表达式的结果。函数返回类型是void的情况1.直接写return因为return后边也可以什么都没有用于让函数提前返回; 2.直接不写return用于只是执行函数内容的情况。return语句执行后函数就彻底返回后边的代码不再执行。return返回的值和函数返回类型不一致系统会自动将返回的值隐式转换为函数的返回类型。如果函数中存在if等分支的语句则要保证每种情况下都有return返回否则会出现编译错误。函数的返回类型如果不写编译器会默认函数的返回类型是int不存在四舍五入小数点后面直接截断。函数写了返回类型但是函数中没有使用return返回值那么函数的返回值是未知的。六.数组做函数参数数组作为参数传递给函数在函数内部对数组进行操作。例如写一个函数将一个整型数组的内容全部置为-1再写一个函数打印数组的内容。基本的形式应该是这样的#includestdio.hintmain(){intarr[]{1,2,3,4,5,6,7,8,9,10};set_arr();//设置数组元素为-1Prinrt_arr();//打印数组return0;}set_arr函数要能够对数组内容进行设置就得把数组作为参数传递给函数同时函数内部在设置数组每个元素的时候也得遍历数组需要知道数组的元素个数。所以我们需要给set_arr传递2个参数一个是数组另外一个是数组的元素个数。print_arr也是一样的只有拿到了数组和元素个数才能遍历打印数组的每个元素。#includestdio.hintmain(){intarr[]{1,2,3,4,5,6,7,8,9,10};size_tszsizeof(arr)/sizeof(arr[0]);//元素个数set_arr(arr,sz);//设置数组元素为-1Prinrt_arr(arr,sz);//打印数组return0;}数组传参的几个重点知识函数的形式参数要和函数的实参个数匹配函数的实参是数组形参也是可以写成数组形式的形参如果是一维数组数组大小可以省略不写形参如果是二维数组行可以省略但是列不能省略C 语言所有参数都是值传递数组传参时传递的值是数组首元素地址俗称地址传参所以形参不会创建新的数组即形参操作的数组和实参的数组是同一个数组。但是普通的数值传参地址改变是临时创建的的新的独立变量出了函数就销毁了。设计函数voidset_arr(intarr[],intsz){for(inti0;isz;i){arr[i]-1;}}voidPrint_arr(intarr[],intsz){for(inti0;isz;i){printf(%d ,arr[i]);}printf(\n);}七.嵌套调用和链式访问一嵌套调用嵌套调用就是函数之间的互相调用。例如假设计算某年某月有多少天如果要函数实现可以设计2个函数:is_leap_year()根据年份确定是否是闰年get_days_of_month()调用is_leap_year确定是否是闰年后再根据月计算这个月的天数#includestdio.h#includestdbool.hboolis_lead_year(inty){if((y%40)(y%100!0)||(y%4000)){returntrue;}else{returnfalse;}}intdays_of_month(inty,intm){intdays[]{0,31,28,31,30,31,30,31,31,30,31,30,31};// 0 1 2 3 4 5 6 7 8 9 10 11 12开头加个0使下标和月份一致则访问对应月份下标即可得对应天数intdaydays[m];if(is_lead_year(y)m2){day1;}returnday;}intmain(){intyear0;intmonth0;scanf(%d%d,year,month);intdaysdays_of_month(year,month);printf(%d\n,days);return0;}main函数调用scanf、printf、get_days_of_monthget_days_of_month函数调用is_leap_year二链式访问链式访问就是将⼀个函数的返回值作为另外⼀个函数的参数像链条⼀样将函数串起来就是函数的链式访问。例如//int printf ( const char * format, ... );printf函数返回的是打印在屏幕上的字符的个数。#includestdio.hintmain(){printf(%d,printf(%d,printf(%d,43)));//4321,return0;//先打印43返回2再打印2返回1再打印1。}八.函数的声明和定义一单个文件C语言编译器对源代码进行编译时是从第一行往下扫描的当遇到函数调用的时候并没有发现前面有该函数的声明就会报出警告。所以函数调用之前要先声明一下这个函数声明函数要交代清楚函数名函数的返回类型和函数的参数。函数声明中参数只保留类型省略掉名字也是可以的。函数的定义是一种特殊的声明。如intis_leap_year(inty)//y可省二多个文件代码比较多时不会将所有的代码都放在一个文件中往往会根据程序的功能将代码拆分放在多个文件中。一般情况下函数的声明、类型的声明放在头文件.h中函数的实现放在源文件.c文件中。例如add.c//函数的定义intAdd(intx,inty){returnxy;}add.h//函数的声明intAdd(intx,inty);test.c#includestdio.h#includeadd.h//包含自定义的头文件 包含标准库中头文件intmain(){inta10;intb20;//函数调⽤intcAdd(a,b);//因为包含了add.h头文件而该头文件里有Add函数的声明在调用之前有函数的声明所以可以调用该函数。printf(%d\n,c);return0;}三static和externstatic和extern都是C语言中的关键字。static是“静态的”可以用来修饰局部变量修饰全局变量修饰函数extern用来声明外部符号。在认识static和extern之前要了解一下作用域和生命周期。作用域scope是程序设计概念一段程序代码中所用到的名字并不总是有效可用的而限定这个名字的可用性的代码范围就是这个名字的作用域。局部变量的作用域是变量所在的局部范围。全局变量的作用域是整个工程项目。生命周期指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的一个时间段。局部变量的生命周期是进入作用域变量创建生命周期开始出作用域生命周期结束。全局变量的生命周期是整个程序的生命周期。1.static修饰局部变量static修饰局部变量改变了变量的生命周期生命周期改变的本质是改变变量的存储类型。局部变量存储在内存的栈区但是被static修饰后存储到了静态区。存储在静态区的变量和全局变量是一样的生命周期就和程序的生命周期一样了只有程序结束变量才销毁内存才回收。但是作用域不变还是只能在其局部范围内使用。使用建议未来一个变量出了函数后还想保留值等下次进入函数继续使用就可以使用static修饰。例如原函数#includestdio.hvoidtest(){intn10;n;printf(%d ,n);}//此处为局部变量进入这个函数变量n创建生命周期开始打印后出函数变量n销毁生命周期结束intmain(){inti0;for(i0;i5;i){test();}//11 11 11 11 11}static修饰后#includestdio.hvoidtest(){staticintn10;//static修饰局部变量改变了变量的⽣命周期,在出函数时不销毁还保留值注作用域不变还是只能在其局部范围内使用n;printf(%d ,n);}intmain(){inti0;for(i0;i5;i){test();}//11 12 13 14 15}静态区的变量初始化仅执行一次。static int n 10;这行代码只有第一次调用 test 的时候会发现它还没初始化执行这条语句之后就跳过这个初始化语句。2.static修饰全局变量⼀个全局变量被static修饰则这个全局变量只能在本源文件内使用不能在其他源文件内使用。本质原因全局变量默认具有外部链接属性在外部的文件中想使用只要适当的声明就可以使用但是全局变量被static修饰之后外部链接属性就变成了内部链接属性只能在自己所在的源文件内部使用了其他源文件即使声明了也是无法正常使用的。使用建议如果一个全局变量只想在所在的源文件内部使用不想被其他文件发现就可以使用static修饰。例如test1:inta100;//test2中声明了可以正常使用//static int a100;//即使test2中声明了也无法使用在编译的时候会出现链接性错误。test2:#includestdio.hexterninta;intmain(){printf(%d\n,a);return0;}3.static修饰函数static修饰函数和static修饰全局变量是一模一样的一个函数在整个工程都可以使用被static修饰后只能在本文件内部使用其他文件无法正常的链接使用了。本质原因函数默认具有外部链接属性所以函数在整个工程中只要适当的声明就可以被使用。但是被static修饰后变成了内部链接属性函数只能在自己所在源文件内部使用。使用建议一个函数只想在所在的源文件内部使用不想被其他源文件使用就可以使用static修饰。例如test1:intAdd(inta,intb){returnab;}//test2中声明了可以正常使用//static int Add(int a, int b)//{// return a b;//}//即使test2中声明了也无法使用在编译的时候会出现链接性错误。test2:#includestdio.hexternintAdd(inta,intb);intmain(){inta0;intb0;scanf(%d %d,a,b);printf(%d,Add(a,b));return0;}4.extern声明外部符号extern是用来声明外部符号的如果一个全局的符号在test1文件中定义的在test2文件中想使用就可以使用extern进行声明然后使用。例如test1.c:inta100;test2.c:#includestdio.hexterninta;//变量a定义在test1.c中在test2.c文件中声明全局变量,整个test2.c工程都能使用voidtest()//定义一个无参数无返回值的函数调用时也能直接用全局变量{printf(3.a%d\n,a);}intmain(){{printf(1.a%d\n,a);}printf(2.a%d\n,a);test();}//正常打印1.2.3.