一、函数的概念
1、函数的概念
每个C程序都至少有一个函数,即main主函数 ,如果程序的任务比较简单,全部的代码都写在main函数中,但是,在实际开发中,程序的任务往往比较复杂,如果全部的代码都写在main函数中,main函数体将非常庞大臃肿,代码重复。
我们可以把程序的任务分工到不同的子函数中,main更关心业务逻辑和处理流程,需要执行某任务的时候,调用子函数就可以了。
2、函数的分类
为了方使理解,我们把函数分为库函数和自定义函数(子函数)。
库函数是C语言提供的,实现了某些基本的功能,例如scanf、printf,在程序中可以直接使用。
自定义函数是程序员为了完成某项任务而编写的函数,目的是为了实现某项的功能或让主程序更简洁。自定义函数在使用之前,必须先声明和定义。
二、自定义函数的声明
自定义函数的声明包括了返回值、函数名和参数列表。有些程序员把函数声明说成是函数原型,只是用词不同,意思是一样的。
C语言中的声明函数的语法如下:
return_type function_name( parameter list );
1)返回值的数据类型return_type:函数执行完任务后的返回值,可以是int、char、double或其它自定义的数据类型。如果函数只执行任务而不返回值,return_type用关键字 void表示,如下:
void function_name( parameter list );
2)函数名function_name:函数名是标识符,命名规则与变量相同。
3)参数列表parameter list:当函数被调用时,调用者需要向函数传递参数。参数列表包括参数的数据类型和书写顺序。参数列表是可选的,也就是说,函数可以没有参数,如下:
return_type function_name();
函数声明示例:
// 判断超女身材函数的声明
// 函数名:checksc
// 参数:height,身高,单位cm。
// 返回值:0-不合格;1-娇小;2-标准;3-高挑
int checksc(int height);
声明了一个函数,返回值是int类型,函数名是checksc,函数只有一个参数int height,注意,函数的声明语句最后的分号不能少。
如果把自定义函数比喻成工具,函数的声明就是这个工具的设计图纸。
三、自定义函数的定义
自定义函数是工具,如果想让这个工具能被使用,光有设计图纸还不行,还要把工具制造出来,函数的定义就是这个工具的实际主体,是为了实现函数的功能而编写的代码。
C语言中的函数定义的语法如下:
return_type function_name( parameter list ) // 注意,不要在函数定义的最后加分号。
{
// 实现函数功能的代码
}
函数定义的return_type、function_name和parameter list必须与函数声明一致。
函数主体包含为了完成任务需要执行语句的集合,放在花括号内。
示例
// 判断超女身材函数的定义
int checksc(int height)
{
if ( (height>=160) && (height<165) ) return 1; // 娇小
if ( (height>=165) && (height<175) ) return 2; // 标准
if ( (height>=175) && (height<180) ) return 3; // 高挑
return 0; // 不合格
}
四、自定义函数代码的组织
1、非通用能功的函数
如果自定义函数只在调用者程序中使用,可以在调用者程序中声明和定义,声明一般为调用者程序的上部,定义一般在调用者程序的下部,这并不是C语言的规定,而是为了让程序更方便阅读,程序员约定的写法。
示例(book45.c)
/*
* 程序名:book45.c,此程序演示自定义函数在主程序中声明和定义
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <string.h>
// 判断超女身材函数的声明
// 函数名:checksc
// 参数:height,身高,单位cm。
// 返回值:0-不合格;1-娇小;2-标准;3-高挑
int checksc(int height);
int main()
{
int ii,jj;
printf("请输入超女的身高(单位:厘米):");
scanf("%d",&ii);
jj=checksc(ii); // 调用checksc函数判断身材
if (jj==0) printf("不合格。
");
if (jj==1) printf("娇小。
");
if (jj==2) printf("标准。
");
if (jj==3) printf("高挑。
");
}
// 判断超女身材函数的定义
int checksc(int height)
{
if ( (height>=160) && (height<165) ) return 1; // 娇小
if ( (height>=165) && (height<175) ) return 2; // 标准
if ( (height>=175) && (height<180) ) return 3; // 高挑
return 0; // 不合格
}
在book45.c中,判断超女身材的函数(checksc)不是公共的功能,只在选秀程序使用,所以它的声明和定义都写在book45.c中。
2、通用能功的函数
如果自定义函数是一个通用的功能模块,可以在公共的头文件中声明,在公共的程序文件中定义。
如果某程序需要调用公共的函数,在调用者程序中用#include指令包含公共的头文件,编译的时候把调用者程序和公共的程序文件一起编译。
在以下的示例中,函数min和max是公共的函数,在_public.h头文件中声明,在_public.c程序文件中定义,在book46.c程序中被调用。
1)公共的头文件(_public.h)
/*
* 程序名:_public.h,公共功能函数声明的头文件
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 声明min函数,用于比较两个整数的大小,取小者
int min(const int ii1,const int ii2);
// 声明max函数,用于比较两个整数的大小,取大者
int max(const int ii1,const int ii2);
2)公共的程序文件(_public.c)
/*
* 程序名:_public.c,公共功能函数声明的程序文件
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "_public.h" // 包含自定义函数声明的头文件
// 用于比较两个整数的大小,函数返回较小者
int min(const int ii1,const int ii2) // min函数定义
{
if (ii1<ii2) return ii1;
return ii2;
}
// 用于比较两个整数的大小,函数返回较大者
int max(const int ii1,const int ii2) // max函数定义
{
if (ii1>ii2) return ii1;
return ii2;
}
3)调用者程序文件(book46.c)
/*
* 程序名:book46.c,此程序演示了在主程序的外部文件中声明和定义函数
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "_public.h" // 把_public.h头文件包含进来
int main()
{
int xx,yy,imin,imax;
xx=50; yy=80;
imin=min(xx,yy);
imax=max(xx,yy);
printf("imin=%d,imax=%d
",imin,imax);
}
4)编译运行
3、注意事项
1)在book46.c的第5行#include "_public.h"
,这里包含头文件是用双引号,不是尖括号<>,这两者的差别如下:
#include <>
用于包含系统提供的头文件,编译的时候,gcc在系统的头文件目录中寻找头文件。
#include ""
用于包含程序员自定义的头文件,编译的时候,gcc先在当前目录中寻找头文件,如果找不到,再到系统的头文件目录中寻找。
2)编译程序的时候,要把调用者程序和公共程序文件一起编译,否则编译器会报错。
3)C语言对公共函数的头文件(_public.h)和程序文件(_public.c)的命名没有规定,由程序员自己命名,为了增加程序的可读性,尽可能采用一些有意义的文件名。
五、库函数
C语言提供了很多标准函数(C standard library),简称库函数,调用这些函数可以完成一些基本的功能,例如printf、scanf、memset、strcpy等。C语言的库函数有几百个,常用的不超过30%,在以后的章节中我将详细介绍常用库函数的应用场景和使用方法。
C语言标准库函数的声明的头文件存放在/usr/include目录中,如下:
<asset.h> <ctype.h> <errno.h> <float.h> <limits.h>
<locale.h> <math.h> <setjmp.h> <signal.h> <stdarg.h>
<stddef.h> <stdlib.h> <stdio.h> <string.h> <time.h>
C语言库函数的定义的文件是/usr/lib/gcc/x86_64-redhat-linux/4.4.4/libgcc.a
(不同编译器的版本目录名有所不同),这是一个打包好的库文件,把程序文件打包成库文件的方法以后再详细介绍。
我们用gcc编译程序的时候并没有把libgcc.a包含进来,那是因为gcc编译器缺省会包含它,但是,程序员自定义函数的程序文件就没有这种待遇,需要手工的包含进来与程序一起编译。
六、需要包含哪些头文件
我们在使用库函数的时候,如果不包含它的头文件,编译时会出现警告或错误,如下:
那么,在我们编写的程序中,应该包含哪些头文件呢?有两种方法,一是上百度上查资料,二是使用Linux系统提供的帮助,以strcpy函数为例,在命令行下输入man strcpy回车,如下:
man显示了函数的声明的头文件(第5行),函数的参数、使用方法和返回值。
注意了,如果程序中调用了库函数但没有包含它头文件,编译的时候不一定是警告,也可能是报错,函数无法识别等,这个要看编译器,不同C语言的编译器的处理方式不一样。
七、调用函数的方法
库函数是系统提供的工具,自定义函数是程序员自己补充的工具,对使用者来说都一样,没有区别。
在C语言中,函数的调用非常灵活,可以独占一行语句,也可以当成常量赋值给变量,也可以当成函数的参数。
如果函数的返回值是void,表示该函数的调用一定会成功,一般用单行书写,独占一条语句。
如果函数的返回值不是void,可以不关心它的返回值,也可以当成常量用于任何表达式中。
示例(book47.c)
/*
* 程序名:book47.c,此程序演示了函数的灵活调用。
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include "_public.h" // 把_public.h头文件包含进来
int main()
{
int i=max(30,45);
max(30,20); // 没有接收返回值,max的活白干了。
55; // 这是什么鬼?
i=max(min(45,38),50);
i=min(max(min(45,38),50),25);
printf("30和50相比,更大的值是%d。
",max(30,50));
}
大家可能对这个代码不理解,一句话:如果函数的返回值是整数,就可以把函数当成整数来用,如果返回值是小数,就可以把函数当成小数来用,……。
八、函数调用的过程
在程序中调用子函数时,程序的流程进入子函数的代码中,当子函数返回时(或到达函数的结束括号时),程序的流程回到调用者程序。
示例(book48.c)
/*
* 程序名:book48.c,此程序演示了函数的调用的过程
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <string.h>
// 声明func函数,无返回值无参数
void func();
int main()
{
printf("即将调用func函数。
");
func();
printf("调用func函数已完成。
");
return 0;
}
void func()
{
int ii=0;
for (ii=0;ii<5;ii++)
{
sleep(1); // sleep是C语言的库函数,睡眠
printf("已经过去了%d秒。
",ii+1);
}
}
运行效果
九、函数参数的传递
函数的参数可以理解为函数内部的变量,参数传递就是调用者程序把变量(或常量)的值复制一份给函数的参数,简单说来就是复制的过程。一旦完成数据的复制,调用者程序的变量和函数的参数就再也没有瓜葛了,所以,在函数调用的过程中,函数内部变量的值发生改变并不会影响调用者程序的变量的值。
我们在调用函数的时候,不关心函数参数的名称,函数参数的名称是函数自己的事情,是函数的内部变量,只在函数内部使用,与调用者无关。
示例(book49.c)
/*
* 程序名:book49.c,此程序演示了函数参数的传递
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
void funcld(int b); // 声明函数funcld,无返回值
int main()
{
int a=10;
printf("调用funcld前a的值是%d.
",a);
funcld(a);
printf("调用funcld后a的值是%d.
",a);
}
void funcld(int b)
{
printf("赋值前b的值是%d.
",b);
b=20;
printf("赋值后b的值是%d.
",b);
}
运行效果
关于函数的参数,在很多教程中有很多说法,如“形参”、“实参”、“值传递”、“地址传递”等,这些说法把简单的概念复杂化了,大家不必理会。
十、const约束
const 是 constant 的缩写,意思是“恒定不变的”,它是定义只读变量的关键字。用 const 定义只读变量的方法很简单,就在定义变量时前面加 const 即可,如:
const double pi = 3.1415926;
用 const 定义的变量的值是不允许改变的,不允许给它重新赋值,即使是赋相同的值也不可以。所以说它定义的是只读变量。这也就意味着必须在定义的时候就给它赋初值,如果程序中试图改变它的值,编译的时候就会报错。
在变量前加const约束,主要用于定义函数的参数,表示该参数在函数中是只读取,不允许改变,如果函数中试图改变它的值,编译的时候就会报错。
例如:
编译如下:
为什么要在函数的参数前加const?有两个目的:1)防止程序员犯错,如果程序员犯了错误,编译器就能发现;2)增加了源代码的可读性。在实际开发中,函数的参数不加const不会有任何问题,但是,程序员一般都会为只读变量加上const约束,这是一个好的习惯。
十一、应用经验
1、如何寻找函数
在我的课程中,会介绍常用库函数的使用方法,但也有漏掉的。查资料能力是程序员的基本技能,它可以提升您解决问题的能力。
例如要查找C语言复制字符串功能的函数,在百度上输入“C语言复制字符串”搜索就可以了,您想查什么就输入什么。
然后,打开多几个网页看看,基本上能找到答案。
注意一个问题,网上的内容太多太杂乱,大部分文章是初学者发的博客,重点关注百度百科的文章。
2、测试函数的功能
以strcpy函数为例,函数的声明如下。
char *strcpy(char *dest, const char *src);
函数功能:字符串复制。
函数名:strcpy。
dest参数:目标字符串。
src参数:待复制的内容,有const约束。
返回值:返回dest,这是提高函数可用性的常用的处理方法。
用代码测试一下:
char strname[50];
strcpy(strname,"西施");
printf("=%s=
",strname);
printf("=%s=
",strcpy(strname,"西施"));
然后,看看输出的结果是不是“=西施=”,用=号夹住输出的内容是为了看清楚有没有其它的字符。
十二、课后作业
1)编写示例程序,自定义一个根据超女的胸围判断身材的函数,判断标准由您定,类似book45.c。
2)C语言的库函数肯定是不够用的,程序员必须要有自己的函数库,从本章节就开始准备,创建您自己的函数库的头文件和程序文件,头文件和程序文件的命名由您定,先放两个函数进去,声明如下,然后写示例程序调用它们。
// 声明max函数,取两个数值的较大者
int max(int ii1,int ii2);
// 声明min函数,取两个数值的较小者
int min(int ii1,int ii2);
这两个自定义函数的功能虽然简单,但是,千里之行,始于足下,慢慢积累。
3)本章节的内容非常重要,建议大家看多几遍视频,用程序测试每个知识点,充分的理解它。
十三、版权声明
C语言技术网原创文章,转载请说明文章的来源、作者和原文的链接。
来源:C语言技术网(www.freecplus.net)
作者:码农有道
如果文章有错别字,或者内容有错误,或其他的建议和意见,请您留言指正,非常感谢!!!