函数的概述

C 程序是由函数组成的,我们写的代码都是由主函数 main()开始执行的。函数是 C 程序的基本模块,是用于完成特定任务的程序代码单元。

从函数定义的角度看,函数可分为系统函数和用户定义函数两种:
(1)系统函数,即库函数:这是由编译系统提供的,用户不必自己定义这些函数,可以直接使用它们,如我们常用的打印函数printf()。

(2)用户定义函数:用以解决用户的专门需要。

为什么要使用函数呢?

1)函数的使用可以省去重复代码的编写。

如果程序中需要多次使用某种特定的功能( 求两个数的最大值 ),直接写在main()里,代码的重复率很比较高,同时,代码给人的感觉会很冗余,如下:

#include <stdio.h>int main()
{// 操作1 ……// ……int a1 = 10, b1 = 20, c1 = 0;if(a1 > b1){c1 = a1;}else{c1 = b1;}// 操作2 ……// ……int a2 = 11, b2 = 21, c2 = 0;if(a2 > b2){c2 = a2;}else{c2 = b2;}// ……return 0;
}

使用函数后,降低代码的重复率,如下:

#include <stdio.h>// 求两数的最大值
int max(int a, int b)
{if(a > b){return a;}else{return b;}
}int main()
{// 操作1 ……// ……int a1 = 10, b1 = 20, c1 = 0;c1 = max(a1, b1); // 调用max()// 操作2 ……// ……int a2 = 11, b2 = 21, c2 = 0;c2 = max(a2, b2); // 调用max()// ……return 0;
}

2)函数可以让程序更加模块化,从而有利于程序的阅读,修改和完善。

假如我们编写一个实现以下功能的程序:读入一行数字;对数字进行排序;找到它们的平均值;打印出一个柱状图。

如果我们把这些操作直接写在main()里,这样可能会给用户感觉代码会有点凌乱。但,假如我们使用函数,这样可以让程序更加清晰、模块化。

#include <stdio.h>int main()
{float list[50];// 这里只是举例,函数还没有实现readlist(list,50);sort(list,50);average(list,50);bargraph(list,50);return 0;
}

这里我们可以这么理解,程序就像公司,公司是由部门组成的,这个部门就类似于C程序的函数。默认情况下,公司就是一个大部门( 只有一个部门的情况下 ),相当于C程序的main()函数。如果公司比较小( 程序比较小 ),因为任务少而简单,一个部门即可( main()函数 )胜任。但是,如果这个公司很大( 大型应用程序 ),任务多而杂,如果只是一个部门管理( 相当于没有部门,没有分工 ),我们可想而知,公司管理、运营起来会有多混乱,不是说这样不可以运营,只是这样不完美而已,如果根据公司要求分成一个个部门( 根据功能封装一个一个函数 ),招聘由行政部门负责,研发由技术部门负责等,这样就可以分工明确,结构清晰,方便管理,各部门之间还可以相互协调。

接下来我们一起学习函数:函数的定义,函数的调用,函数的声明。

函数的定义

函数定义的一般形式:

返回类型 函数名( 形式参数列表 )

{

数据定义部分;

执行语句部分;

}

例子如下图:

这里还有几点说明:

1)函数名:理论上是可以随意起名字,最好起的名字见名知意,应该让用户看到这个函数名字就知道这个函数的功能。注意,函数名的后面有个圆换号(),代表这个为函数,不是普通的变量名。

2)形参列表:在定义函数时指定的形参,在未出现函数调用时,它们并不占内存中的存储单元,因此称它们是形式参数或虚拟参数,简称形参,表示它们并不是实际存在的数据,所以,形参里的变量不能赋值。

int max(int a = 10, int b = 20) // error, 形参不能赋值
{}

在定义函数时指定的形参,必须是,类型+变量的形式:

///1: right, 类型+变量
int max(int a, int b)
{}///2: error, 只有类型,没有变量
int max(int, int)
{}///3: error, 只有变量,没有类型
int a, int b;
int max(a, b)
{}

在定义函数时指定的形参,可有可无,根据函数的需要来设计,如果没有形参,圆括号内容为空,或写一个void关键字:

// 没形参, 圆括号内容为空
int max()
{}// 没形参, 圆括号内容为void关键字
int max(void)
{}

3)函数体:花括号{ }里的内容即为函数体的内容,这里为函数功能实现的过程,这和以前的写代码没太大区别,以前我们把代码写在main()函数里,现在只是把这些写到别的函数里。

4)返回类型:函数的值是指函数被调用之后,执行函数体中的程序段所取得的并返回给主调函数的值。函数的返回值是通过函数中的return语句获得的,return后面的值也可以是一个表达式。

a)尽量保证return语句中表达式的值和函数返回类型是同一类型。

int max() // 函数的返回值为int类型
{int a = 10;return a;// 返回值a为int类型,函数返回类型也是int,匹配
}

b)如果函数返回的类型和return语句中表达式的值不一致,则以函数返回类型为准,即函数返回类型决定返回值的类型。对数值型数据,可以自动进行类型转换。

int max() // 函数的返回值为int类型
{double a = 10.1f;return a;// 返回值a为double类型,它会转为int类型(a = 10)再返回
}

如果函数返回的类型和return语句中表达式的值不一致,而它又无法自动进行类型转换,程序则会报错。

c)return语句的另一个作用为中断return所在的执行函数,类似于break中断循环,switch语句一样。

int max()
{return 1;// 执行到,函数已经被中断,所以下面的return 2无法被执行到return 2;// 没有执行
}

d)如果函数带返回值,return后面必须跟着一个值,如果函数没有返回值,函数名字的前面必须写一个void关键字,这时候,我们写代码时也可以通过return中断函数(也可以不用),只是这时,return后面不带内容( 分号“;”除外)。

void max()// 必须要有void关键字
{return; // 中断函数,这个可有可无
}

函数的调用

定义函数后,我们需要调用此函数才能执行到这个函数里的代码段。这和main()函数不一样,main()为编译器设定好自动调用的主函数,无需人为调用,我们都是在main()函数里调用别的函数,一个 C 程序里有且只有一个main()函数。

我们有这么一个简单程序:

#include <stdio.h>int main()
{printf("this is for test\n");return 0;
}

接着,我们将这个打印的操作封装在自定义函数里,这个函数即没返回值,也没形参:

#include <stdio.h>void print_test()
{printf("this is for test\n");
}int main()
{print_test();  // print_test函数的调用return 0;
}

这个程序运行的效果,和上面的是等价的。

程序的执行流程:

1)进入main()函数

2)调用print_test()函数:

a) 它会在main()函数的前寻找有没有一个名字叫“print_test”的函数定义;

b) 如果找到,接着检查函数的参数,这里调用函数时没有传参,函数定义也没有形参,参数类型匹配;

c) 开始执行print_test()函数,这时候,main()函数里面的执行会阻塞( 停 )在print_test()这一行代码,等待print_test()函数的执行。

3)print_test()函数执行完( 这里打印一句话 ),main()才会继续往下执行,执行到return 0, 程序执行完毕。

C语言中,函数调用的一般形式为:

函数名(实际参数表)

1)实际参数表,常称“实参”, 这里要区别好形参和实参,函数定义时圆括号里的是形参,函数调用时圆括号里的是实参。

a)如果是调用无参函数,则不能加上“实参”,但括号不能省略。

// 函数的定义
void test()
{}int main()
{// 函数的调用test();    // right, 圆括号()不能省略test(250); // error, 函数定义时没有参数return 0;
}

b)如果实参表列包含多个实参,则各参数间用逗号隔开。

// 函数的定义
void test(int a, int b)
{}int main()
{int p = 10, q = 20;test(p, q);   // 函数的调用return 0;
}

c)实参与形参的个数应相等,类型应匹配(相同或赋值兼容)。实参与形参按顺序对应,一对一地传递数据。

d)实参可以是常量、变量或表达式,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。所以,这里的变量是在圆括号( )外面定义好、赋好值的变量。

// 函数的定义
void test(int a, int b)
{}int main()
{// 函数的调用int p = 10, q = 20;test(p, q);   // righttest(11, 30-10); // righttest(int a, int b); // error, 不应该在圆括号里定义变量return 0;
}

2)函数的返回值

a)如果函数定义没有返回值,函数调用时不能写void关键字,调用函数时也不能接收函数的返回值。

// 函数的定义
void test()
{}int main()
{// 函数的调用test(); // rightvoid test(); // error, 函数调用时不能写void关键字int a = test(); // error, 函数定义根本就没有返回值return 0;
}

b)如果函数定义有返回值,这个返回值我们根据用户需要可用可不用,但是,假如我们需要使用这个函数返回值,我们需要定义一个匹配类型的变量来接收。

// 函数的定义, 返回值为int类型
int test()
{}int main()
{// 函数的调用int a = test(); // right, a为int类型int b;b = test();   // right, 和上面等级char *p = test(); // error, p为char *, 函数返回值为int, 类型不匹配int = test();    // error, 必须定义一个匹配类型的变量来接收返回值,// int只是类型,没有定义变量int test();// error, 必须定义一个匹配类型的变量来接收返回值,// int只是类型,没有定义变量return 0;
}

下面,我们写一个完整例子:

#include <stdio.h>
/****************************
功能:求两个数的最大值
参数:x, y: 需要比较的两个整数
返回值:两个数的最大值
******************************/
int max(int x, int y)
{return x>y ? x : y;
}int main()
{int a = 10, b = 25, num_max = 0;num_max = max(a, b); // 函数的调用printf("num_max = %d\n", num_max);return 0;
}

当调用max(a, b)时,实参变量(a, b)对形参变量(x, y)的进行数据传递( “值传递” )。只有在发生函数调用时,函数max中的形参这时候才会被分配内存单元,以便接收从实参传来的数据。在调用结束后,形参所占的内存单元也被释放。同时,把函数的返回值赋给main()里的num_max变量。

关于形参和实参的几点说明:

1)形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。

2)实参出现在主调函数中,进入被调函数后,实参变量也不能使用。

3)实参变量对形参变量的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回来给实参。

4)在调用函数时,编译系统临时给形参分配存储单元。调用结束后,形参单元被释放。

5)实参单元与形参单元是不同的单元。调用结束后,形参单元被释放,函数调用结束返回主调函数后则不能再使用该形参变量。实参单元仍保留并维持原值。因此,在执行一个被调用函数时,形参的值如果发生改变,并不会改变主调函数中实参的值。

函数的声明

在一个函数中调用另一个函数(即被调用函数)需要具备哪些条件呢?

1)首先被调用的函数必须是已经存在的函数。

#include <stdio.h>int main()
{int a = 10, b = 25, num_max = 0;num_max = max(a, b); // 函数的调用,这个函数不存在printf("num_max = %d\n", num_max);return 0;
}

编译程序会报错:

2)如果使用库函数,包含所需要头文件即可用。

#include <stdio.h>
#include <string.h> // strlen()所需头文件int main()
{char *p = "hello, I am mike!"int len = strlen(p); // 测字符串的长度printf("len = %d\n", len);return 0;
}

3)如果使用用户自己定义的函数,而该函数与调用它的函数(即主调函数)在文件中,且函数定义的位置在主调函数之后,则必须在调用此函数之前对被调用的函数作声明。

所谓函数声明,就是在函数尚在未定义的情况下,事先将该函数的有关信息通知编译系统,相当于告诉编译器,函数在后面定义,以便使编译能正常进行。

函数声明其一般形式为( 后面的分号不能省略 ):
类型说明符 被调函数名(类型 形参,类型 形参 …);
或为:
类型说明符 被调函数名(类型,类型 …);

括号内给出了形参的类型和形参名,或只给出形参类型。这便于编译系统进行检错,以
防止可能出现的错误。

如:
int max(int a,int b);
或写为:
int max(int,int);

完整例子如下:

#include <stdio.h>int max(int x, int y); // 函数的声明,分号不能省略
// int max(int, int); // 另一种方式int main()
{int a = 10, b = 25, num_max = 0;num_max = max(a, b); // 函数的调用printf("num_max = %d\n", num_max);return 0;
}// 函数的定义
int max(int x, int y)
{return x>y ? x : y;
}

函数定义和定义的区别:

1)定义是指对函数功能的确立,包括指定函数名、函数类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。

2)声明的作用则是把函数的名字、函数类型以及形参的个数、类型和顺序(注意,不包括函数体)通知编译系统,以便在对包含函数调用的语句进行编译时,据此对其进行对照检查(例如函数名是否正确,实参与形参的类型和个数是否一致)。

C语言入门学习——函数的使用相关推荐

  1. 大学生学习c语言方法,针对在校大学生的C语言入门学习——函数

    为什么要使用函数如果你没有接受过系统的训练就能主动想要封装函数,说明你在编程上是一个天才,千万不要辜负自己的天赋!函数在语法上并不难理解,但是什么时候使用函数才合理呢? 我们不妨来做一个猜谜游戏--& ...

  2. c语言入门自学免费app,C语言入门学习最新版下载-C语言入门学习app手机版v1.0.2 安卓版-腾飞网...

    C语言入门学习app手机版是一款c语言编程自学软件,零基础也可以学习,里面有海量教学视频,针对c语言不同程度的讲解都囊括其中.随时随地学习编程都可以,不用担心自己没有基础.还支持在手机上敲代码编程哦. ...

  3. c语言入门自学手机版,c语言入门自学app下载-C语言入门学习 安卓版v1.0.2-PC6安卓网...

    C语言入门学习app是一款C语言零基础自学软件.C语言入门自学app提供海量精品学习资源,从小白入门到基础进阶都有,帮你轻松学习编程. 软件介绍 C语言入门学习app是一款专业的编程入门学习App,致 ...

  4. c语言入门自学手机版,C语言入门学习app下载-C语言入门学习app最新版下载 V1.0.2-友情手机站...

    C语言入门学习app是一款0基础自学软件,这里有着丰富C语音相关课程学习,大家在这里是可以便捷搜索查找,随时都是可以找到适合感兴趣课程学习,都是一些优质课程知识提供大家,学员在这里是可以高效学习,海恩 ...

  5. c语言入门自学手机版,C语言入门学习软件下载-C语言入门学习手机版v1.0.2 - 起点软件园...

    C语言入门学习是一款最新推出上线于安卓手机平台的专业编程学习软件,C语言入门学习app收录了海量入门视频课程,清晰易懂的详细讲解专为刚入门学习C语言的小白量身打造,不同章节都有相应的习题,根据习题成绩 ...

  6. C语言-入门级别函数详解

    C语言-入门级别函数详解 写在开始 关于函数 1. 函数的定义形式 2.函数的声明 3. 返回语句 4.函数参数 4.1 形式参数(传值调用) 4.2 实际参数(传址调用) 4.3无参数 5.函数的调 ...

  7. lua语言入门学习(二)lua语言的特点及注意事项

    lua语言入门学习 lua语言的特点及注意事项 文章目录 lua语言入门学习 lua语言的学习地址 一.lua语言特点 1.注释 2.全局变量和局部变量 3.lua数据类型 4.字符串类型 5.多变量 ...

  8. lua语言入门学习(四)项目初体验之抽奖转盘(1)

    lua语言入门学习 今天前辈教我看了项目,感觉果然公司级的项目分工都比较严谨,很多东西都是略懂功能,但对于底层逻辑的实现并不清楚. 为了保护公司这个游戏具体实现功能上本身的隐私,日常代码分享中不会分享 ...

  9. lua语言入门学习(八)项目初体验之玩家事件(2)

    lua语言入门学习 今天是实习的第二周,前辈给我的任务是熟悉playerevent中每个函数的基本功能. 文章目录 lua语言入门学习 前言 一.代码 1.代码功能自己简析 1.代码功能自己简析 2. ...

最新文章

  1. 码农的祖宗是个妹子?!信不信由你!
  2. JS-数据类型-数值Number
  3. SpringBoot-服务端参数验证-JSR-303验证框架
  4. 《编程之美》(Java实现) :让CPU占用率画直线和正弦曲线(Java实现)
  5. Tensorflow Day19 Denoising Autoencoder
  6. mysql 分表 sphinx_sphinx(coreseek)处理分表的方案
  7. 构建创业公司突击小团队
  8. 【渝粤教育】电大中专混凝土结构作业 题库
  9. 从零搭建基于知识图谱的问答系统(以医疗行业为例)
  10. PHP生成唯一会员卡号
  11. idea解决lombok注解失效问题
  12. fastjson反序列化map_最新fastjson反序列化漏洞分析
  13. 台式计算机计量单位,计算机常见计量单位解析
  14. mysql 当前用户连接数,查看mysql当前连接数的方法详解
  15. VCS命令行选项总结简单脚本实例
  16. ThreadPool中变量ctl的分析
  17. 数据结构-顺序表基本操作的实现(含全部代码)
  18. 将Ubuntu装入移动硬盘
  19. hololens2 通信 之 Windows.Networking.Sockets
  20. 百度细雨算法2.0正式上线

热门文章

  1. u盘系统文件删除后的五种恢复方法
  2. IAR C语言嵌入汇编问题
  3. 手机如何操作打印学习资料
  4. 腾讯云----首次登录提示“腾讯云实例鉴权失败,请确认实例已启用密码鉴权并且账号密码正确”
  5. dedecms5.7织梦cms自动给文章加内链 utf-8版
  6. 如何构建基于Python的星座脚本
  7. 米老师讲课 ITOO4.1 ——Java(2)
  8. 关于spark yarn模式下的常用属性
  9. HTTP协议常问的面试题(吐血整理)
  10. 数据库用impdp导入时日志报错:ORA-39246, ORA-39059,哪位大神给解答一下