资讯专栏INFORMATION COLUMN

【C语言】玩转函数——关于函数,你需要知道的知识!

newsning / 3015人阅读

摘要:可分为两类,一类是语言标准规定的库函数,一类是编译器特定的库函数。函数的调用传值调用函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。函数的声明一般出现在函数的使用之前。函数的声明一般要放在头文件中的。


前言

函数在我们编程的时候,发挥着巨大的作用。它就像一个功能,一个招式,当我们需要用的时候,直接调用出来抵挡对手。函数的使用,既让我们的代码变得简洁了,也让我们的代码变得方便管理。因此,在学习编程的时候,掌握函数这一知识点无疑是在我们学习占据重要地位的。

1、 C语言中的函数是什么?

在维基百科中,对C语言中函数的定义是:子程序。
那么什么是子程序呢?

1、在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,subprogram, callable unit),是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。

2、一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。

从中提取信息点,“大型程序中的某部分代码” “负责完成某项特定任务” 。举个例子说,就比如我们在做计算器的时候,需要加减乘除四个大块内容,我们在这里就可将加法做成一个多带带的代码块,专门用来实现加法的功能。这功能就是函数。

2、函数的分类

2.1库函数

2.1.1库函数是什么?

库函数(Library function)是把函数放到库里,供别人使用的一种方式。是把一些常用到的函数编完放到一个文件里,供不同的人进行调用。调用的时候把它所在的文件名用#include<>加到里面就可以了。一般是放到lib文件里的。

一般是指编译器提供的可在c源程序中调用的函数。
可分为两类,一类是c语言标准规定的库函数,一类是编译器特定的库函数。

由于版权原因,库函数的源代码一般是不可见的,但在头文件中你可以看到它对外的接口。

举个例子:

  1. 当我们在学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格式打印到屏幕上(使用printf函数)。
  2. 在编程的过程中我们会频繁的做一些字符串的拷贝工作(使用strcpy函数)。
  3. 在编程是我们也计算,总是会计算n的k次方这样的运算(使用pow函数)。

像上面我们描述的基础功能,它们不是业务性的代码。我们在开发的过程中每个程序员都可能用的到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员 进行软件开发。

2.1.2如何学习库函数

在这里,我们可以用到一个程序员必备网站:http://www.cplusplus.com/

打开,我们可以看到有非常多的库函数
我们在这里以strcpy为例来给大家讲解一下如何使用这个网站。

第一步,打开网站
第二步,搜索函数strcpy

第三步,查看函数功能与用法。
例子代码:

#include#includeint main(){	char arr1[20] = { 0 };	char arr2[] = "gogogo";	strcpy(arr1, arr2);	printf("%s/n", arr1);}

相信通过这样的讲述,大家也能明白怎样使用库函数了吧!

在这里,简单的总结,C语言常用的库函数都有:

IO函数——输入输出函数 input/output printf scanf getchar putchar
字符串操作函数 strlen strcmp strcpy strcat
字符操作函数 tolower toupper
内存操作函数 memcpy menset memmove memcmp
时间日期函数 time
数学函数 sqrt abs fabs pow
其他库函数

注意:
但是库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。

需要学会查询工具的使用:
MSDN(Microsoft Developer Network)
www.cplusplus.com
http://en.cppreference.com(英文版) http://ch.cppreference.com(中文版)

2.2自定义函数

大家有没有发现,库函数并不能解决所有的问题,比如说我今天要写一个自动刷牙的程序,可能库函数中根本就没有。这时候,就需要我们程序员自己来定义函数了

这就是自定义函数。
自定义函数和库函数一样,有函数名,返回值类型和函数参数。
但是不一样的是这些都是我们自己来设计。这给程序员一个很大的自由发挥的空间。

ret_type fun_name(para1, * )
{
statement;//语句项
}

ret_type 返回类型
fun_name 函数名
para1 函数参数

举个例子:

写一个函数找出两个整数中的最大值

#include //get_max函数的设计int get_max(int x, int y) { return (x>y)?(x):(y);}int main(){ int num1 = 10; int num2 = 20; int max = get_max(num1, num2);//调用函数 printf("max = %d/n", max); return 0;  }

3、函数的参数

3.1实际参数(实参)

在调用有参函数时,主调函数和被调函数之间有数据传递关系。
在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”(简称“实参”)。
实参可以是常量、变量或表达式, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。

举个例子:

#include void Swap1(int x, int y) { int tmp = 0; tmp = x; x = y; y = tmp; }void Swap2(int *px, int *py) { int tmp = 0; tmp = *px; *px = *py; *py = tmp; }int main(){ int num1 = 1; int num2 = 2; Swap1(num1, num2);//实参可以是常量、变量或表达式 printf("Swap1::num1 = %d num2 = %d/n", num1, num2); Swap2(&num1, &num2);//实参可以是常量、变量或表达式 printf("Swap2::num1 = %d num2 = %d/n", num1, num2); return 0; }

3.1形式参数(形参)

形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。
形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。

上面Swap1和Swap2函数中的参数 x,y,px,py 都是形式参数。
在main函数中传给Swap1的num1,num2和传给Swap2函数的&num1,&num2是实际参数。

这里我们对函数的实参和形参进行分析
借用网上的一幅图

实参是调用函数传递的具体数据。实参对形参数据传递时时单向传递。在存储单元中是不同的单元.

在上面的Swap1函数,在调用过程中,形参x和y的值都发生了改变,但是在main函数中,num1和num2的值都未发生变化。所以说实参向形参的值的传递是单向的。

Swap2函数,由于把地址也传过去了,所以两个值都改变了,这也是下面所讲的传值调用与传址调用。

**【注意】**形参实例化之后其实相当于实参的一份临时拷贝。

4、函数的调用

4.1传值调用

函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。

举个例子,交换两个整数的值。

#include int Swap(int x, int y)//这里形参x,y占据的内存块与下面实参a,b占据的内存块是不同的{	int z = 0;	z= x;	x= y;	y = z;}int main(){	int a = 10;	int b = 20;	printf("交换前a=%d,b=%d/n", a, b);	Swap(a, b);//这里实参a,b占据的内存块与上面形参x,y占据的内存块是不同的	printf("交换后a=%d,b=%d/n", a, b);	return 0;}

我们可以看到,这里a与b的值并没有发生变化,因为**函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。**所以,实参a和b,传给形参x,y的时候,形参是实参的一份临时拷贝,改变形参变量x,y,是不会影响实参a和b.

4.2传址调用

1、传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
2、这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。

举个例子,也交换两个整数的值

#includevoid Swap(int *px, int *py)//用指针接收a,b的地址{	int z = 0;	z = *px;	*px = *py;	*py = z;}int main(){	int a = 10;	int b = 20;	printf("交换前a=%d,b=%d/n", a, b);	Swap(&a,&b);//把a,b的地址传到上面函数去	printf("交换后a=%d,b=%d/n", a, b);	return 0;}

在这里,我们可以了解到,如果把a和b的内存地址传过去给函数的时候,在函数中,我们就可以依照这个指针地址,传数据回来。让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。

5、 函数的嵌套调用和链式访问

5.1嵌套使用

需要说明的是,C语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下一级函数的问题。但是C语言允许在一个函数的定义中出现对另一个函数的调用。这样就出现了函数的嵌套调用。即在被调函数中又调用其它函数。

举个例子:

void first_line(){	printf("hello world/n");}void two_line(){	int i = 0;	first_line();}int main(){	two_line();	return 0;}

main函数调用了two_line函数,而two_line函数调用了first_line函数。函数调用了函数,我们就说是函数的嵌套调用
**注意,**函数可以嵌套调用,但是不能嵌套定义。
什么意思?
就是说,我们不能在函数之中定义一个函数,举个例子:

5.2链式访问

把一个函数的返回值作为另外一个函数的参数。

我们举个例子。

#include int main(){    printf("%d/n", printf("%d", printf("%d", 43)));    //注:printf函数的返回值是打印在屏幕上字符的个数    return 0; }

6、函数的声明与定义

6.1函数的声明

  1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数 声明决定不了。
  2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
  3. 函数的声明一般要放在头文件中的。

函数声明的格式:

dataType functionName( dataType1 param1, dataType2 param2 … );

有了函数声明,函数定义就可以出现在任何地方了,甚至是其他文件、静态链接库、动态链接库等。

简单来说,“函数声明”的作用是把函数的名字、函数类型以及形参类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查(例如函数名是否正确,实参与形参的类型和个数是否一致)

6.2函数定义

函数的定义是指函数的具体实现,交待函数的功能实现。

void Swap1(int *px, int *py);//函数的声明void Swap(int *px, int *py)//函数定义{	int z = 0;	z = *px;	*px = *py;	*py = z;}int main(){	int a = 10;	int b = 20;	printf("交换前a=%d,b=%d/n", a, b);	Swap(&a,&b);	printf("交换后a=%d,b=%d/n", a, b);	return 0;}

6.3函数声明与定义的区别

1、函数的声明与函数的定义形式上十分相似,但是二者有着本质上的不同。声明是不开辟内存的,仅仅告诉编译器,要声明的部分存在,要预留一点空间。定义则需要开辟内存。

函数的定义
函数的定义是一个完整的函数单元:

包含函数类型、函数名、形参及形参类型、函数体等在程序中,函数的定义只能有一次函数首部与花括号间不加分号

函数的声明
函数声明只是对编译系统的一个说明:

函数声明是对定义的函数的返回值的类型说明,以通知系统在本函数中所调用的函数是什么类型。不包含函数体(或形参)调用几次该函数就应在各个主调函数中做相应声明函数声明是一个说明语句,必须以分号结束!

总结

函数还有很多其他的知识,本文只是略作探讨。函数在我们编程之中占据重要地位,只有学好了函数,我们才能在接下的学习之中更好的前进。


!!! 感谢各位看到这里,小弟不才,文中若有错误之处,还望指出,共同探讨,共同进步!!!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/119675.html

相关文章

  • C语言玩转指针——关于指针,需要掌握基础知识

    摘要:如果我们要将的地址保存到中,我们需要我们给指针变量相应的类型。类型的指针是为了存放类型变量的地址这里可以看到,指针的定义方式是类型名指针变量名。如下最后本文介绍的是指针的基础知识,往后还会继续深入讲解指 ...

    zebrayoung 评论0 收藏0
  • C语言玩转递归——学好递归,需要掌握知识

    摘要:所以,递归在编程中同样是很重要的一个知识点。举个例子用递归实现求第个斐波那契数。总结起来四个字大事化小继续举斐波那契数的例子三递归是怎样运行的我们通过一道题目来讲解。 ...

    Donne 评论0 收藏0
  • 面试官:我们只想要这样C++工程师

    摘要:今天先奉上一篇关于方向的,,,,前端,测试这些方向的文章随后也会出来,欢迎大家关注呀一什么是研发工程师呢是最贴近底层编程语言。因此大部分公司的核心业务都是用进行开发,工程师掌握着公司最核心最重要的业务体系,有着十分重要的地位。 showImg(https://segmentfault.com/img/bVbv8Cq); 最近好多同学来找就就说,我想找 xxx 方向的工作,我怎么学习呀?...

    cppowboy 评论0 收藏0
  • 玩转 React(一)- 前言

    摘要:本人计划编写一个针对中初级前端开发者学习的系列教程玩转。使用的原因是新的语言规范开发效率更高代码更优雅,尤其是基于开发的项目。其次也是目前特别流行的一个前端框架,截止目前,上有将近万,国内一二线互联网公司都有深度依赖开发的项目。 本人计划编写一个针对中初级前端开发者学习 React 的系列教程 - 《玩转 React》。 文章更新频率:每周 1 ~ 2 篇。 目录 玩转 React(...

    waltr 评论0 收藏0
  • ES6-7

    摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...

    mudiyouyou 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<