C语言-函数(一):函数的定义和调用

article/2025/5/8 9:07:31

函数是一个完成特定工作的独立程序模块,包括库函数和自定义函数两种。例如,scanf()、printf()等这些都为库函数,是由C语言系统提供定义,编程时直接调用即可;还有一种是自己定义的函数,我们主要介绍的就是这类函数。

1、函数的定义

C语言的函数与数学上的函数概念类似。例如计算圆柱体积的公式为 V = π r 2 h V=\pi r^2h V=πr2h,在数学上可以写成 V = f ( r , h ) V=f(r,h) V=f(r,h)这个二元函数,其中 r , h r,h r,h是自变量, V V V是根据自变量所得到的函数值。在C语言中同样可以定义一个函数double cylinder(double r, double h),这个函数的功能就是计算圆柱的体积。其函数值的结果依赖于 r , h r,h r,h,在C语言中称为函数参数。而 f ( r , h ) f(r,h) f(r,h)计算得到的函数值,在C语言中一定要为某一种数据类型,称为函数类型。
函数定义的一般形式:

	函数类型 函数名(形式参数表)	/*函数首部*/{函数实现过程				/*函数体*/}

例如计算圆柱体积函数的定义:

double cylinder(double r, double h)
{double result;result = 3.1415926*r*r*h;	/*计算圆柱体积*/return result;				/*返回结果*/
}

(1)函数首部
从上面函数定义的一般形式可以看到,函数首部是由函数类型、函数名和形式参数表(简称形参表)组成,位于函数定义的第一行。其中,函数名是函数整体的称谓,它需要用一个合法的标识符表示(同变量)。函数类型是指函数结果最后要返回什么类型,一般与return语句中表达式的类型一致。形参表中给出函数计算所要用到的相关已知条件,以类似变量定义的形式给出,其格式为:

	类型1 形参1, 类型2 形参2, ..., 类型n 形参n

形参表中各个形参之间用逗号(这里的逗号仅仅是标点符号,而不是逗号运算符)分隔,每个形参前面的类型必须写明。形参的数量可以是一个,也可以是多个,还可以没有形参。
函数首部后面是不能加分号的,函数首部和函数体在一起构成了完整的函数的定义。如上面计算圆柱体积的例子,函数首部为:

double cylinder(double r, double h)

这里函数的类型是double,就是最后函数的结果类型;函数名是cylinder;该函数有两个形参r和h,它们的数据类型都是double,计算圆柱的体积时依赖这两个形参。cylinder()函数被调用时,这两个形参的值会由主调函数给出。要注意,形参的类型不能省略,这里写成double r, h错误的。
(2)函数体
函数体由一对大括号内的所有语句组成,其作用是实现该函数的具体功能,最后return语句返回函数的结果。例如上例的函数体为:

{double result;result = 3.1415926*r*r*h;	/*计算圆柱体积*/return result;				/*返回结果*/
}

return后面的表达式应该要和函数的类型一致(不一致会发生隐式类型转换,后面有讲),例如该函数的类型是double型,result也是double型的。这里result是普通的变量,不是形参,它是该函数实现过程中需要用到的变量,只有函数圆括号内的变量才是形参。

例:计算圆柱体积

输入圆柱的高和半径,求圆柱体积 v o l u m e = π × r 2 × h volume=\pi\times r^2\times h volume=π×r2×h。要求定义和调用函数cylinder(r,h)计算圆柱体的体积。

/*计算圆柱体积*/
#include <stdio.h>double cylinder(double r, double h);	/*函数声明*/int main(void)
{double height, radius, volume;printf("Enter radius and height:");	scanf("%lf%lf", &radius, &height);	/*输入圆柱的半径和高度*/volume = cylinder(radius, height);	/*调用函数,返回值赋给volume*/printf("volume = %.3f\n", volume);	/*输出圆柱的体积*/return 0;	
}	/*定义求圆柱体积的函数*/
double cylinder(double r, double h)
{double result;result = 3.1415926*r*r*h;		/*计算圆柱体积*/return result;					/*返回结果*/
}

2、函数的调用

函数定义后就可以使用了。在C语言中,调用标准库函数时,只需要在程序的最前面用#include命令包含相应的头文件即可;调用自定义函数时,程序中必须有与被调用函数相对应的函数定义。

(1)函数的调用过程
任何C语言执行,首先从主函数main()开始,如果遇到某个函数调用,主函数被暂停执行,转而执行相应的函数,该函数执行完后将返回主函数,然后再从原先暂停的位置继续执行。
下面根据上例看一下函数的调用过程:

上图中的序号为程序的执行流程:

①main()函数运行到:volume = cylinder(radius, height);时调用cylinder()函数,这里main()函数暂停执行,将变量radius和height的值传递给形参r和h;
②开始执行cylinder()函数,其中形参r和h分别接收变量radius和height的值;
③执行cylinder()函数中的语句,计算圆柱体积;
④函数cylinder()执行return result;语句后,结束函数运行,带着函数的结果result,返回到main()函数中调用它的地方。
⑤计算机从先前暂停的位置继续执行,将返回值赋给变量volume,输出体积。程序结束。
通常把调用其他函数的函数称为主调函数,在这里main()函数就是主调函数;被调用的函数称为被调函数,这里就是cylinder()函数。
(2)函数的调用的形式
函数调用的一般形式为:

	函数名(实际参数表);

实际参数(简称实参)可以是常量、变量和表达式。但是数据类型应该要保持相同,虽说一般不会报错,但是很有可能会隐含一些隐蔽的逻辑错误,要养成良好的编程习惯。例子中变量radius和height就是实参。
带返回值的函数可以当做一个表达式使用,一般如下两种调用方式:
①赋值语句
即此时函数作为右值:

volume = cylinder(radius,height);

②输出函数的值
此时函数可以当做一个表达式被函数调用,这个函数的结果就是它的返回值:

printf("%f", cylinder(radius, height));

3、参数传递
函数首部的参数称为形参。主调函数传给形参的参数称为实参。形参除了能接受实参的值外,使用方法与普通的变量一样。形参与实参必须一一对应,数量和顺序都一致,且类型也要尽量一致。函数调用时,实参的值会依次传给形参。
上例中从函数首部double cylinder(double r, double h)可以知道形参是r和h,main()函数调用时volume = cylinder(radius, height);的实参就是radius和height,它们和r、h一一对应,值也会依次传递给r、h。
函数的形参必须是变量,用于接受实参传递过来的值;而实参可以是常量、变量或者表达式,其作用是把常量、变量或者表达式的值传递给形参。形参和实参可以同名。
传递参数过程中,是将实参的值复制给形参。这种参数传递是单向的,只允许实参把值复制给形参,形参的值即使在函数中改变了,也不会影响到实参。

4、函数结果返回
函数结果的返回形式如下:

	return 表达式;

在返回过程中,会先求解表达式的值,再返回其值。一般来说这里表达式的类型应该与函数类型保持一致,若不一致,则以函数的类型为准。
return语句的作用有两个:一是结束函数的运行;二是带着运算结果(表达式的值)返回主调函数。注意:return语句只能返回一个值。

5、函数原型声明
C语言规定,函数要先定义后调用。如果自定义函数放在了主调函数后面,那么需要在函数调用前,加上函数原型声明(也称函数声明)。
函数声明的目的是说明函数的类型和参数的情况,保证程序编译时能判断对该函数的调用是否正确。函数声明的一般格式为:

	函数类型		函数名(参数表);

一般情况是与函数定义中的第一行(函数首部)相同,并以分号结束。这里的参数表也可以仅有参数的类型,没有参数的名称。例如double cylinder(double r, double h);这是一句函数声明,也可以写成double cylinder(double, double);即仅需要形参的数据类型。为了增强可读性,一般都会带上参数名。
注意函数声明和定义的区别:声明是一条C语言语句,而定义时的函数首部不是语句,后面不能跟分号。
函数声明的位置:可以在主调函数内,也可以在主调函数外。若是在主调函数内,则在函数声明后的主调函数语句才可以使用,或者函数定义后面的函数可以调用。

3、函数的优点

(1)程序结构清晰,逻辑关系明确,程序可读性强;
(2)解决相同或相似问题时不用重复编写代码,可通过调用函数来解决,减少代码量;
(3)利用函数实现模块化编程,各模块功能相对独立,利用“各个击破”降低调试难度。

例1:计算五边形的面积

将一个五边形分割成3个三角形,输入这些三角形的7条边长,计算边长为x、y、z的三角形面积。

#include <stdio.h>
#include <math.h>int main(void)
{double a1, a2, a3, a4, a5, a6, a7, s;double area(double x, double y, double z);	/*函数声明*/printf("Please input 7 side lengths in the order a1 to a7:\n");scanf("%lf%lf%lf%lf%lf%lf%lf", &a1, &a2, &a3, &a4, &a5, &a6, &a7); s = area(a1, a5, a6)+area(a4, a6, a7)+area(a2, a3, a7);		/*调用3次area函数*/printf("The area of the Pentagon is %.2f\n", s);return 0;
}/*使用海伦-秦九韶公式计算三角形面积的函数*/
double area(double x, double y, double z)	/*函数首部*/
{double p = (x+y+z)/2;return sqrt(p*(p-x)*(p-y)*(p-z));
}

Please input 7 side lengths in the order a1 to a7:
3.6 3.6 3.6 3.6 3.6 4.5 4.5
The area of the Pentagon is 20.07

这里的定义了一个函数area(),用海伦-秦九韶公式来计算三角形的面积。函数的声明放在了主调函数里面。

例2:判断完全平方数

定义一个判断完全平方数的函数IsSquare(n),当n为完全平方数时返回1,否则返回0,不允许调用数学库函数。
如果n是完全平方数,则可以找到正整数m,使 m = n 2 m=n^2 m=n2成立。在不使用函数sqrt()的情况下,我们也可以判断一个数是否为完全平方数。例如,当n是完全平方数时,则n可以采用以下等差数列求和公式计算。
1 + 3 + 5 + 7 + … + ( 2 ∗ m − 1 ) = m 2 = n 1+3+5+7+…+(2*m-1)=m^2=n 1+3+5+7++(2m1)=m2=n

#include <stdio.h>
int IsSquare(int n);int main(void)
{int n;printf("Enter n:");scanf("%d", &n);if (IsSquare(n)){printf("%d is a Square!\n", n);} else {printf("%d is not a Square!\n", n);}return 0;	
}			/*判断完全平方数的函数*/
int IsSquare(int n)		/*函数首部*/
{int i;for (i=1; n>0; i=i+2){n = n-i;}if (n==0){return 1;		/*是完全平方数返回1*/} else {return 0;		/*不是完全平方数返回0*/}
}

上述函数中有两个return语句,只要函数执行了return语句后,这个函数就会结束运行,后面的语句就不会执行了。同时在主调函数可以发现,最后n的值没有改变,即函数内部不会对实参产生影响。

例3:辗转相除法求最大公约数

/*采用辗转相除法求最大公约数的函数*/	
int gcd(int m, int n)	/*定义求最大公约数函数gcd()*/
{int r, temp;if (m < n){			/*如果m小于n,则交换m和n的值*/temp = m;m = n;n = temp;}r = m%n;while (r != 0){m = n;n = r;r = m%n;}return n;
}

辗转相除法,又称欧几里得算法,是求最大公约数的一种常用方法。具体做法是:用较大的数m除以较小的数n,再用出现的余数(第一余数)去除除数,再用出现的余数(第二余数)去除第一余数,如此反复,知道最后余数为0为止,那么最后的除数就是这两个数的最大公约数。这是一个迭代算法:
(1)用m除以n,得到余数赋值给r;
(2)如果r为0,则返回n的值作为结果并结束;否则进入第(3)步;
(3)将n的值赋给m,将余数r赋给n,返回第(1)步。

例4:判断素数

/*判断素数的函数*/
int prime(int m)
{int i, limit;if(m <= 1){			/*小于等于1的数不是素数*/return 0;} else if (m == 2){	/*2是素数*/return 1;} else {			/*其他情况:大于2的正整数*/limit = sqrt(m)+1;for (i=2; i<=limit; i++){if (m%i == 0){return 0;		/*若m能被某个i整除,则m不是素数,返回0*/}}/*若循环正常结束,说明m不能被任何一个i整除,则m是素数,返回1*/return 1; }
}

定义了一个判断素数的函数。若一个数能被 [ 2 , m ] [2, \sqrt m ] [2,m ]之间任意一个数整除,则该数不是素数;否则为素数。

参考 C语言程序设计(第4版)/何钦铭,颜晖
例题及课后习题参考程序https://gitee.com/sgxgitee/mooc-c


http://chatgpt.dhexx.cn/article/KrAyxbuK.shtml

相关文章

全网最细!软件测试—bug生命周期

测试人员最本质的工作就是寻找bug&#xff0c;提交bug、验证bug、推进bug的解决&#xff0c;直至软件达到发布的标准&#xff0c;提高软件的质量&#xff0c;及研发的工作效率和质量。 一、什么是bug 软件的BUG,狭义概念是指软件程序的漏洞或缺陷&#xff0c;广义概念除此之外还…

bug的生命周期你知道吗?一张图带你看懂它!

目录 1、什么是bug 2、bug的生命周期 3、如何描述一个bug 4、bug的级别 1、什么是bug 软件的bug狭义方面可以理解为是指软件程序的漏洞或缺陷&#xff0c;广义方面除找到程序漏洞之外&#xff0c;还包括测试工程师或用户所发现和提出的软件可改进的细节、或与需求文档存在差…

如何描述一个BUG以及BUG级别的定义、生命周期

目录 一、如何描述一个BUG&#xff1f; 二、BUG级别的定义 1.崩溃 2.严重 3.一般 4.次要 三、BUG的生命周期 一、如何描述一个BUG&#xff1f; 1.版本号&#xff08;代码版本号&#xff09; 2.测试环境/平台 不同的浏览器对同一个页面解析是不一样的&#xff1a; ①…

一个BUG(缺陷)的生命周期

缺陷状态 对于一个问题&#xff0c;其处理过程是一个周期&#xff0c;周期的不同阶段&#xff0c;其所处的状态也是不一样的。不同状态所对应的处理人也是不一样的。 打开 &#xff1a; 表示问题被提交等待有人处理。 重新指派 &#xff1a; 问题被重新指派给某人处理。 处理 …

bug生命周期bug跟踪处理

一、BUG BUG&#xff1a;软件的缺陷 1.BUG的定义&#xff1a;----与软件测试的目的对应 软件的BUG,狭义概念是指软件程序的漏洞或缺陷&#xff0c;广义概念除此之外还包括测试工程师或用户所发现和提出的软件可改进的细节、或与需求文档存在差异的功能实现等。 我们的职责就是&…

软件测试bug生命周期

测试人员最本质的工作就是寻找bug&#xff0c;提交bug、验证bug、推进bug的解决&#xff0c;直至软件达到发布的标准&#xff0c;提高软件的质量&#xff0c;及研发的工作效率和质量。 一、什么是bug 软件的BUG,狭义概念是指软件程序的漏洞或缺陷&#xff0c;广义概念除此之外…

简述Bug生命周期与Bug管理

Bug&#xff0c;即在测试过程中发现的问题&#xff0c;是测试工程师绩效最重要的考核之一&#xff0c;也是面试常被问到的知识领域。 今天&#xff0c;从Bug的生命周期入手&#xff0c;跟大家聊聊Bug管理。 Bug的生命周期如下所示&#xff0c;其中&#xff0c;新建、分配&…

(四)Bug的生命周期

Bug的属性 Bug重现环境 这个应该是我们重现bug的一个前提&#xff0c;如果没有这个前提&#xff0c;我们可能会无法重现问题&#xff0c;或者跟本就无从下手。 操作系统 这个是一般软件运行的一大前提&#xff0c;基本上所有的软件都依赖于操作系统之上的&#xff0c;对于一…

面试题之Bug的生命周期

今天来分享一个基础面试题&#xff1a;Bug的生命周期&#xff0c;虽然基础&#xff0c;但是面试被问到的概率很高。 Bug的生命周期&#xff0c;也就是一个Bug被发现到被关闭的过程&#xff0c;在整个生命周期中&#xff0c;不同的阶段对应不同的缺陷状态。 大家可以参考如下的…

测试之Bug与用例【创建Bug、Bug级别、Bug生命周期、测试用例的万能公式、设计测试用例具体方法】

文章目录 1. 如何创建Bug2. Bug的级别3. Bug的生命周期4. 面试题&#xff1a;跟开发产生争执怎么办5. 设计测试用例的万能公式使用万能公式对水杯设计测试用例 6. 设计测试用例的具体方法6.1 等价类6.2 边界类6.3 判定表6.4 正交法(allparis)6.5 场景设计法 1. 如何创建Bug 提…

Bug生命周期

过程描述&#xff1a; 1. 测试工程师开始执行测试&#xff0c;发现bug则新建bug&#xff0c;这时bug是新建状态。 2. 测试组负责人把bug修改为打开状态&#xff0c;表明开发人员可以修改该bug了。之所以会有打开这一步有两点目的&#xff1a;第一是测试组负责人对bug进行确认…

详解BUG(又名:BUG的生命周期)

测试人员最本质的工作就是寻找bug&#xff0c;提交bug、验证bug、推进bug的解决&#xff0c;直至软件达到发布的标准&#xff0c;提高软件的质量&#xff0c;及研发的工作效率和质量。 一、什么是bug 软件的BUG,狭义概念是指软件程序的漏洞或缺陷&#xff0c;广义概念除此之外…

Bug的生命周期状态流程图

bug的生命周期 BUG的生命周期&#xff0c;就是一个BUG被发现到这个BUG被关闭的过程。 生命周期中缺陷状态&#xff1a;新建-->指派-->已解决-->待验-->关闭 发现BUG-->提交BUG-->指派BUG-->研发确认BUG-->研发去修复BUG-->回归验证BUG-->是否通过…

BUG的生命周期,bug从“出生到死亡”全部过程

前言&#xff1a; 基本定义&#xff1a;BUG从发现到这个bug关闭&#xff0c;是一个完整的生命周期。 一&#xff1a;从具体状态上来讲 状态有这几种&#xff1a; 1&#xff1a;new-bug被第一次发现的时候&#xff0c;确认是一个问题&#xff0c;将bug进行记录。 2&#xff…

bug的生命周期都有那些阶段

bug的生命周期都有那些阶段 一、主要有八个生命周期阶段二、bug生命周期流程图 一、主要有八个生命周期阶段 1&#xff09;New&#xff1a; 是新发现的bug&#xff08;指给对应的开发&#xff09;&#xff1b;   2&#xff09;Open&#xff1a; 开发确认这是bug&#xff0c;并…

测试基础-05-bug的定义生命周期

1 学习目标&内容 1.1 bug的类型 1.2 bug的等级 1.3 bug的生命周期 1.4 禅道的使用 2 bug的定义 软件的Bug&#xff1a;狭义概念是指软件程序的漏洞或缺陷&#xff0c;广义概念除此之外还包括测试工程师或用户所发现和剔除的软件可改进的细节或与需求文档存在差异的功…

bug的生命周期

bug的生命周期 测试人员的主要职责什么是bugbug的生命周期1、发现bug2、提交bug3、指派bug4、确认缺陷5、修复BUG6、回归验证BUG7、关闭缺陷 管理bug的工具首先是国内的bug管理软件&#xff1a;国外的bug管理软件有&#xff1a; 测试人员的主要职责 测试人员最本质的工作就是寻…

IDEA:Windows 下载安装 IDEA 详细教程

一、下载IDEA 官网 首先去IDEA官网中文版&#xff1a;IntelliJ IDEA 点击下载按钮进入下载页面 下载 下载社区版&#xff0c;社区版是免费的&#xff0c;相比于收费版少了些功能&#xff0c;不过日常开发也够用了 二、安装 第一步 下载完成后双击安装包&#xff0c;点…

Linux系统创建桌面快捷方式,安装idea,配置idea环境

一、下载Linux版IDEA 使用浏览器打开IDEA官网的链接&#xff1a;https://www.jetbrains.com/idea/或https://www.jetbrains.com/idea/download/other.html 使用wget命令下载 wget wget https://下载地址.tar.gz创建目录 #在home文件夹中创建develop文件夹&#xff0c;再在…

新电脑 安装idea迁移设置和插件注意事项

设置备份 首先&#xff0c;旧电脑Idea导出设置 在新电脑导入设置 注意1&#xff1a;只能导出常规设置&#xff0c;并不能导出插件 注意2&#xff1a;如果新电脑使用的Java、Maven、Git的安装位置不同&#xff0c;需要对相应位置修改 Java路径修改 Maven路径修改 Git路径修改…