目录

  • 前言——小颖的话
  • 一、函数的概念
  • 二、C语言中函数的分类
    • 1.库函数
    • 1.自定义函数
      • 1)函数名
      • 2)函数返回值
      • 3)函数参数
      • 4)实现几个简单函数
  • 三、函数的形参与实参
    • 1.实际参数
    • 2.形式参数
  • 四、函数的调用
    • 1.函数的嵌套调用
    • 2.函数的链式访问
    • 3.函数的递归
  • 五、函数的声明和定义
  • 总结

前言——小颖的话

上一篇呢我们学习了C语言中的控制流-选择和循环,今天我们来学习C语言中另一个非常重要的部分——函数,可以说函数在程序中占到了举足轻重的作用,所以赶快打开你的VS,和小颖一起学习这一部分的知识。


提示:正文开始,学习过程中跟着讲解一起打代码会事半功倍哦

一、函数的概念

其实函数的定义我们在第二篇博客里已经浅略地介绍过了。这里再啰嗦一遍。我们常把函数也叫作子程序,什么意思呢,就是相当于整个程序是由着一个个“儿子程序”组成的。就相当于是一辆车,这一辆车其实是很多不同部件组合而成的,比如说轮子啊,车窗,油箱等等。在不同的地方将一个个零部件先生产出来,然后再统一组合,就可以组成一个有着强大性能的汽车了。
这样做的好处就是可以提高效率,减少成本。

所以在C语言里我们也可以把经常使用到的功能打包成一个函数,提高效率。

二、C语言中函数的分类

C语言中有以下两种函数:
1.库函数
2.自定义函数

1.库函数

我们在打印某些数据时候,经常在程序里会打上这样一段话,

printf("...");

其实这个就是在使用C语言的库函数。库函数就是别人写好的,你可以直接使用的函数,比如你打印时使用的printf函数,输入时候scanf函数,幂运算pow函数
这些功能是程序员常使用到的一些,所以为了提高程序运行的效率,C语言的基础库里就提供了这样一些库函数,可以直接使用(但是记得引用对应头文件)
那么有哪些库函数呢?下面这个网站提供了很多库函数,可以直接看到函数参量啊,返回值,用法等等内容,下面是网站链接。C plusplus
从上图中reference一栏中我们大概可以发现,库函数大概包含这些类型:
1.io函数(输入/输出函数)
2.字符串操作函数
3.字符操作函数
4.内存操作函数
5.时间/日期函数
6.数学函数
7.其他

那我们有了这样一个参考资料,如何用这个网站来学习库函数呢?接下来我们来举例学习:
点击这个string.h头文件,找到strcpy函数

然后下面的图介绍了各种参量在哪里可以参照。在最下面还有example(没截下来),可以仿照里面的例子自己打一个代码

后面在C语言进阶学习里还会对各种函数的使用和模拟实现进行更详细的讲解,在这里就不再赘述strcpy等函数的具体使用注意事项和方法。

1.自定义函数

如果库函数已经可以完成所有的功能,那么还有程序员就没有用了,因为所有人只要会用库函数不就行了?当然这样显然是不实现的,我们生活中更多的问题是原本库函数无法直接解决的,因此程序员就需要发挥自己的创造力和想象力以及C语言的基本语法,去自己写出合适的函数去解决现实问题。

自定义函数的内容包括哪些呢?无非还是这些内容:

  1. 函数名
  2. 函数的返回值
  3. 函数的参数
  4. 函数的具体内容

下面的图是一个函数大概的模型:

1)函数名

函数名的确定是有自己来确定的,注意不要和库函数冲突。一般来说,我们起名字的时候尽量让函数名字更加有意义,比如取最大值函数,我们一般取名字get_max(注意,一般在函数名字里加上下划线会使函数很清晰,等后面学习通讯录,数据结构的时候可以发现这样的命名方式会很舒服)。

2)函数返回值

函数的返回值就是函数运行完之后回到调用函数的位置的时候带回去的值,一个函数最多只能带回一个返回值(在后面会有一个“函数栈帧的创建与销毁”专题会让你更好理解)。那既然牵扯到数值,必然就会涉及到数值的类型,那我们就需要规定一下返回值的数据类型,常用的类型有:

类型 意义
void 无返回值
int 返回整形
float 返回浮点值
double 返回高精度浮点值
type * 返回指针

当然只要是数据可以有的类型,函数返回值就有,这里只举例了常见的一些。(注意type 就是各种类型中的任意一个,包含int ,float 等等,C语言里没有type这个类型,我们一般把这个单词当做类型不确定的时候暂时当做一个已知类型)

在函数的内部一般需要使用 return 关键字来返回一个返回值。在这里注意:
遇到return关键字后立马从函数里跳出,无论后面是否还有代码(因此返回值是void的函数(即无返回值的函数)也可以使用return来强制结束函数的运行)
一个函数最多只能返回一个返回值!!!并且如果返回值类型不为void的函数必须有return来返回一个值。
return后面的数据类型与返回值类型需要保持一致

3)函数参数

函数参数就是在调用函数的时候带进来的数据,可以是零个数据,一个数据或者多个数据(无参数,一个参数,多个参数)。既然是数据,那就必然也有类型,因此和函数的返回值类型一样,函数的参数也要标明类型(在无参数时可以在括号里只写一个void或者在括号里啥都不写)
当然,对于函数参数,还有一些问题,参数的传值传递和传址传递,我们接下来会介绍。

4)实现几个简单函数

学习完了一个函数的基本组成部分,现在我们来实现几个函数
第一个,取最大值函数get_max,要求是给定两个整数,取得两个之中的较大值。
根据描述,我们得到其返回值类型,参数个数和参数类型,可以得到函数的大概样子:

int get_max(int x, int y)
{return
}

至于如何得到最大的值,请读者自己先动脑子想一想,下面是几个参考,
复杂但好想的:

int get_max(int x, int y)
{if (x > y)return x;elsereturn y;
}

简化一下:

int get_max(int x, int y)
{return (x > y) ? (x) : (y);//建议带上括号,防止歧义
}

完整的代码:

#include<stdio.h>
int get_max(int x, int y)
{return (x > y) ? (x) : (y);
}
int main()
{int a = 5;int b = 1;printf("%d", get_max(a, b));return 0;
}

第二个,实现交换函数swap,要求是给定两个整型变量,交换这两个变量的值
根据描述,我们可以得出函数无返回值,参数有两个,类型是整形。
按照一般思路我们会这样写框架:

void swap(int x, int y)
{}

交换两个变量一般的思路是“油醋瓶交换法”
交换油和醋,我们先找到第三个空瓶子,把油倒进去,再把醋倒到油瓶子里,再把油倒到本来装醋的瓶子里,即可完成。
按照这个思路,我们写出这样的代码:

void swap(int x, int y)
{int tmp;//相当于一个空瓶子tmp = x;x = y;y = tmp;
}

整体代码是这样的:

#include<stdio.h>
void swap(int x, int y)
{int tmp;tmp = x;x = y;y = tmp;
}
int main()
{int a = 5;int b = 1;printf("交换前:a = %d ,b = %d\n", a, b);swap(a, b);printf("交换后:a = %d ,b = %d\n", a, b);return 0;
}

然后你运行,发现出意外了

我勒给去,这啥呀,改了和没改一样,是代码思路的问题吗,当然不是,这就牵扯到我们下面要介绍的函数的参数的形参和实参的问题。

三、函数的形参与实参

1.实际参数

我们在调用函数的时候给函数的值,就叫做实参,就比如我们比大小函数中给的a 和b ,这两个参数就是实参
实参可以是:变量,常量,表达式(这就是为什么取最大值函数简化的时候为什么要加上括号),函数等等
但是实参无论是哪一种,都必须要有一个具体的值。

2.形式参数

函数的形式参数就是括号里的参数,是实际参数的一份临时拷贝,在函数调用完之后就被销毁,就相当于演员的替身,在这一段戏演完之后就可以领盒饭了。


那这时候就很好理解为什么上面的交换函数为什么没有解决掉你的问题了,就拿临时演员对比,在武打戏中如果演员用了替身,那么如果意外受伤,那么主演本人并没有受伤,受伤的是替身。

你在交换的时候,交换的是临时创建的一个新的变量而不是原本的a,b,只是把x和y交换了,a,b和x,y的唯一关系就是其类型和数值是一样的,你交换的x和y,在函数结束之后又被销毁,交换并没有任何意义。

那我们该怎么通过函数来改变a和b的值呢??
这里就需要传址
我们知道,一个变量被创建后,一定是有一个固定的地址的,我们如果把这两个变量的地址传递给函数,然后直接通过地址对这两个变量的值直接接操作就可以完成任务了。

这时候函数的框架就要改变了,以为传递的参量的类型发生了改变:
(这时候两个参量应该是整形指针类型)

void swap(int* x, int* y)
{}

这时候函数的样子:

void swap(int* x, int* y)
{int tmp;tmp = *x;*x = *y;*y = tmp;
}

如果到这里看不懂代码,说明一开始的C语言学习总括中的指针那一块没有理解,建议回去重新看一遍。

写出来的整个代码:
(特别注意!!!这时候传参的时候是要把a和b的地址给传过去!!!看下列代码中注释部分)

#include<stdio.h>
void swap(int* x, int* y)
{int tmp;tmp = *x;*x = *y;*y = tmp;
}
int main()
{int a = 5;int b = 1;printf("交换前:a = %d ,b = %d\n", a, b);swap(&a, &b);//这里要取地址!!!printf("交换后:a = %d ,b = %d\n", a, b);return 0;
}

结果如下:

这种通过传递地址从而改变原来的实际参数的方式叫做传址调用
而只用到实参的数值的时候,只把数值传给函数的方式叫做传值调用

四、函数的调用

函数的调用方式无非就是上面的两种:
传值调用
传址调用

可以自行找一些题目来练习,
注:如果想检测自己学习的效果,推荐的刷题网站:PTA,牛客网,LeetCode
这里我们继续介绍一些较为复杂的函数调用方式

1.函数的嵌套调用

我们知道我们的函数一般都是在主函数里调用的,那么我们很容易理解,函数里可以继续调用函数,是不同的函数的套娃

void hello()
{printf("hello\n");
}
void conversation()
{hello();
}
int main()
{conversation();return 0;
}

但是注意:函数里不能够定义函数,所以不存在嵌套定义这一说。

2.函数的链式访问

链式访问,即把一个函数的返回值做为另一个函数的参数
下面是一道典型的例子:

#include<stdio.h>
int main()
{printf("%d", printf("%d", printf("%d", 43)));return 0;
}

(注:printf函数的返回值就是打印在屏幕上的字符的个数。)
问输出是什么?
答案是:
4321
为什么?因为先看最里面的printf("%d", 43),这个printf打印出的是43,返回值是2;然后就是printf("%d", printf("%d", 43))就相当于printf("%d", 2)这个printf打印出2,返回值是1;然后以此类推就知道最后一个了printf打印的是1,结果就是4321了。

3.函数的递归

函数的递归其实就是套娃,自己调用自己,直到满足某个条件。
递归可以将很复杂的问题变成一个简单问题的反复处理,但是不是任何条件下都可以进行递归的,需要有以下这些条件:

  1. 存在限制条件,使得满足这个条件的时候,可以结束递归
  2. 每次递归后都会越来越接近这个限制条件

不然就会导致递归一直进行而使得栈溢出

举个例子来见证递归的厉害:
给定一个无符号整形值,按照顺序依次打印其每一位的数字,比如123,输出:1 2 3
思路:

上代码:

#include<stdio.h>
void print(int n)
{if (n > 9){print(n / 10);}printf("%d ", n % 10);
}
int main()
{int a = 123;print(a);return 0;
}

也许很多小伙伴和小颖一样,一开始根本想不到这样写,也不知道这样写问什么可以做到要求,现在小颖画图给看官来解释解释哈:
图很复杂,记得放大并且跟着序号箭头一步一步看

先理解理解,理解完了继续来一道:
使用递归求n得阶乘
参考代码:

int fac(int n)
{if(n <= 1)return 1;elsereturn n * fac(n - 1);
}

如果没过瘾就上刚刚小颖推荐的几个平台上找题目做,具体的刷题环节如果有时间小颖会出几期。
递归固然很好用,但是问题是占用内存开销太大,因为每调用一次函数,就要在栈上开辟一片空间(后来的函数栈帧的创建与销毁会解释这个)挖坑ing…

五、函数的声明和定义

函数的声明就是告诉编译器有这个函数
函数的声明一般在使用之前,函数的声明一般放在头文件中。
函数定义就是你写出来的函数,是函数的具体实现,交代了函数的功能。。。
无论如何,函数都必须遵循“先声明后使用”的原则
当然,如果你在使用前定义·这个函数,就不需要要额外再声明一次。

void test()//函数的声明
int main()
{test();return 0;
}
void test()//函数的定义
{...
{
void test()
{...
{int main()
{test();return 0;
}

总结

好了,到这里函数的知识就告一段落了,相信看完的你一定对函数有了更深刻的了解。随着接下来的学习,你对函数将会有更加深刻的理解,一切加油吧!

C语言学习——从零开始学编程(第四篇:函数)相关推荐

  1. C语言学习——从零开始学编程(第二篇:C语言知识总括)

    本节将介绍:C语言的基本框架和大部分知识的浅了解(记得先看目录哦~~) 前言--小颖的话 提示:本文篇幅长,知识点多,可以分开学习,记得收藏点赞哦~ 一.数据类型 1.C语言中的关键字 2.不同类型所 ...

  2. C语言学习——从零开始学编程(第一篇:Visual Studio的下载和第一个C语言程序)

    小颖的话: 也许有很多的小伙伴们和小颖一样,曾在玩着马里奥,贪吃蛇,或者打着植物打僵尸的时候想着这些游戏是怎么运作的❓

  3. C语言学习——从零开始学编程(第三篇:选择与循环)

    文章目录 前言--小颖的话 一.语句 1)C语言中的语句有哪些 2)语句 二.选择语句 1) if选择语句 1. if语句 2.if-else语句 3.if - else if - else 多分支语 ...

  4. C语言学习——从零开始学编程(第五篇:数组)

    文章目录 前言--小颖的话 一.一维数组 1.一维数组的创建和初始化 1)一维数组的创建(定义) 2)一维数组的初始化 2.一维数组的使用 3.一维数组在内存中的存储 二.二维数组 1.二维数组的创建 ...

  5. python从0开始学编程课件_小白从零开始学编程(三)--python基本概念

    前言 从2020年3月份开始,计划写一系列文档--<小白从零开始学编程>,记录自己从0开始学习的一些东西. 第一个系列:python,计划从安装.环境搭建.基本语法.到利用Django和F ...

  6. 从零开始学Xamarin.Forms(四) Android 准备步骤(添加第三方Xamarin.Forms.Labs库)

    从零开始学Xamarin.Forms(四) Android 准备步骤(添加第三方Xamarin.Forms.Labs库) 原文:从零开始学Xamarin.Forms(四) Android 准备步骤(添 ...

  7. 《Python深度学习从零开始学》简介

    #好书推荐##好书奇遇季#深度学习入门书<Python深度学习从零开始学>,京东当当天猫都有发售.从模型和实验入手,快速掌握深度学习技术. 业内大咖强力推荐!!!武汉大学信息管理学院教授 ...

  8. 从零开始学 Python 之基础篇

    从零开始学 Python 之基础篇 前言 大家好,这里是「痴海」从零开始学习 Python 系列教程.此文首发于「痴海」公众号,欢迎大家去关注.学习一门语言最好的办法,就是教懂别人.在这公众号,我会从 ...

  9. 从零开始学建站-主机篇

    从零开始学建站-主机篇 主机的基础知识 对于网站来说,主机的意义不同于传统意义的PC.简单地说,主机就是存放网站内容的地方,可以称之为"主机空间"."网站服务器" ...

最新文章

  1. 深入理解java虚拟机之类文件结构以及加载
  2. 数据库mysql爆满怎么搞_MySQL 磁盘满了,怎么办??
  3. Oracle数据库操作注意事项
  4. Mint-Ui的mt-search点击选中
  5. LeetCode 1958. 检查操作是否合法(模拟)
  6. html-简单验证、滑块、搜索框
  7. 项目管理最佳实践方法_项目管理最佳实践,企业如何进行有效的项目管理
  8. java多线程 文件夹_Java多线程遍历文件夹,广度遍历加多线程加深度遍历结合
  9. 北京东二环——东四联通机房
  10. Leetcode10. Regular Expression Matching
  11. 全国计算机等级考试一级试题免费,全国计算机等级考试一级试题
  12. java web项目_一个完整JavaWeb项目开发总结
  13. 强大的代码保护软件 .NET Reactor使用教程(四): .NET Reactor针对De4Dot脱壳工具的应对
  14. javascript错误_您可能会犯的javascript错误
  15. php rrd 源码,ganalia+php+nginx+rrd 监控hadoop
  16. 计算机专业高级职称评定条件,计算机高级工程师职称评定条件
  17. 下一代游戏主机,8GB内存怎么够
  18. CPP全面总结(涵盖C++11标准)
  19. 澳洲值得代购物品汇总
  20. 基于springboot,vue校园点餐系统

热门文章

  1. 【地理空间】轨迹匹配算法学习总结
  2. 感谢信任,我爱你们!
  3. python二进制文件打开出错_Python读二进制文件遇到的问题
  4. java移动端开发教程视频_Java Web开发-项目部分(中国移动科技综合管理系统)视频教程 - JavaWeb - Java - 私塾在线 - 只做精品视频课程服务...
  5. 数学建模学习笔记(1)数学模型的特点和分类
  6. c语言试卷自动批改系统.c
  7. 一款强大的数据库连接工具Navica11
  8. 手机蓝牙小车,教你DIY自己的玩具——硬件篇
  9. Ztree 节点不可选
  10. KEIL软件调节字体大小教程