C语言学习之函数知识总结

 更新时间:2022年7月8日 15:29  点击:205 作者:南森森

前言

函数是C语言的基本单位,学好函数有利于程序的模块化以及避免写出重复的代码从而减少代码量,并且可以提高程序的复用性与可读性。

一、引入

引例:定义6个整型变量a,b,c,d,e,f,并对它们任意赋值。分别输出a,b的最大值,c,d的最大值和e,f的最大值

#include <stdio.h>

int main()
{
    int a, b, c, d, e, f;
    
    a = 5, b = 3, c = 6, d = 87, e = 12, f = 49;

    if (a > b)
        printf("%d\n", a);
    else if (a < b)
        printf("%d\n", b);

    if (c > d)
        printf("%d\n", c);
    else if (c < d)
        printf("%d\n", d);
    
    if (e > f)
        printf("%d\n", e);
    else if (e < f)
        printf("%d\n", f);

    return 0;
}

通过观察可以发现,代码中有大量重复的部分,如果我们定义一万个变量,并两两比较求出其中的最大值,那么if...else if...else语句就要写一万次,这显然非常累赘。在编程过程中,经常会发现,虽然数据不一样,但是对这些数据的操作却是一样的,例如,引例中求a,b中的最大值与求c,d的最大值所做的操作是一样的,唯独只有被操作的数据不同而已。所以如果程序中有大量重复的作操,但只是针对的数据不一样时,我们可以通过定义函数来解决代码重复的问题。上述引例可以通过如下代码实现对应的功能:

#include <stdio.h>

void get_max(int i, int j) // 自定义的求最大值的函数
{
    if (i > j)
       printf("%d\n", i);
   else if (i < j)
       printf("%d\n", j);
   else
       printf("Equal!\n");
}

int main()
{
    int a, b, c, d, e, f;
    
    a = 5, b = 3, c = 6, d = 87, e = 12, f = 49;

    get_max(a, b);
    get_max(c, d);
    get_max(e, f);

    return 0;
}

二、认识函数

void表示该函数没有返回值,get_max是该函数的名字,函数名后括号中的变量i和j被称为该函数的形式参数(简称形参)。

void get_max(int i, int j)
{
    if (i > j)
       printf("%d\n", i);
   else if (i < j)
       printf("%d\n", j);
}

所有的语言程序的入口都是main函数,从main函数进入则开始顺序逐行执行main函数中的代码。下面代码块中定义完变量并完成赋值后,程序执行到get_max(a, b);,此时程序便会从main函数的上方查找一个名叫get_max函数并将括号中的变量a和b的值分别传输到get_max函数名后括号中的形参i和j中,然后程序会跳转到get_max函数的内部执行,待get_max函数执行完毕后,再跳转回main函数继续执行main函数中的下一条语句。

int main()
{
    int a, b, c, d, e, f;
    
    a = 5, b = 3, c = 6, d = 87, e = 12, f = 49;

    get_max(a, b);
    get_max(c, d);
    get_max(e, f);

    return 0;
}

三、函数的作用

函数是一种工具,它是能够完成特定功能的独立代码块,它不是为某个特定的问题设计的,而是为解决大量同类型的问题设计的;虽然函数处理的数据是不同的,但是对这些数据的操作是相同的;函数的使用可以避免写大量重复的代码,减少程序员的编码量;同时函数的存在有利于整个程序的模块化,函数可以将复杂的问题剖解为一个个简单的问题;函数可以被视为一个“黑匣子”,我们只需要知道某个函数的用法即可使用该函数,但我们并不一定知道该函数是怎么实现的,例如,我们虽然经常使用的printf函数,但我们并不知道其中到底是如何实现输出功能的。同样的,当我们编写函数的时候,也可以把具体实现的过程隐藏起来,因为这可能是商业机密。

四、函数的返回值

函数不仅可以接收数据,并对接收到的数据进行处理,而且也可以将数据处理的结果返回,例如下述程序:

#include <stdio.h>

int f(void) // int表示函数的返回值是int类型的值;括号中的void表示该函数不能接收任何数据
{
    return 10; // return语句表示向主调函数返回一个值
}

int main()
{
    int i = 88;
    
    i = f(); // 调用f函数后,f函数就会返回10这个数据,所以这行代码就相当于把10赋值给变量i

    printf("%d\n", i);

    return 0;
}

思考:下述程序是否正确?

#include <stdio.h>

void g(void) // 函数名前的void表示该函数没有返回值
{
}

void h(void)
{
	return 10; // 编译时如果没有调用该函数,则不会报错,一旦调用便会报错,因为h函数的返回值为空
}

int main()
{
    int i = 88;
    
    i = g(); // 由于g函数没有返回值,所以不能将其赋值给变量i,编译时会报错

    printf("%d\n", i);

    return 0;
}

五、定义函数

六、函数的类型

函数返回值的类型也称为函数的类型,如果函数名前的返回值类型与函数执行体中的return表达式的返回的类型不同,则最终函数返回值以函数名前的返回值类型为准。

思考:下述程序输出的结果是多少?

#include <stdio.h>

int f() // 如果函数名后的括号里是空的,等同于在括号里写void
{
	return 10.5;
}

int main()
{
	double x = 6.6;

	x = f();
	
	printf("%lf\n", x);

	return 0;
}

通过程序可得,变量x输出的值为10.000000,这就说明,函数最终的返回值与函数名前的返回值类型相同,而不是以return表达式为准。所以,函数的类型取决于函数名前的返回值的类型。

七、return语句与break语句的区别

break语句的作用是终止当前循环和switch语句,而return语句与break语句有本质性的差别,return语句的作用是终止当前正在执行的函数。当被调函数的返回值为空时,则直接终止被调函数然后跳回主调函数继续顺序执行主调函数中的代码;如果被调函数的返回值不为空时,则先将返回值返回给主调函数,然后再终止被调函数并跳回主调函数继续顺序执行主调函数中的代码。

思考:以下两个程序的输出结果是否相同?如果不同,这两个程序的输出结果分别是什么?

#incldude <stdio.h>

void f()
{
	for (i = 0; i < 5; i++)
	{
		printf("AAAA\n");
		break;
	}
	printf("BBBB\n");
}

int main()
{
	f();
	
	return 0;
}

#incldude <stdio.h>

void f()
{
	for (i = 0; i < 5; i++)
	{
		printf("AAAA\n");
		return;
	}
	printf("BBBB\n");
}

int main()
{
	f();
	
	return 0;
}

八、函数的分类

根据是否有形参可以将函数分为,有参函数和无参函数;根据是否有返回值可以将函数分为有返回值函数和无返回值函数;同时,也可以将函数分为库函数和自定义函数,例如,printf函数就属于库函数,因为是系统提供给我们的,而这篇文章中的get_max函数,f函数,g函数等均属于自定义函数。

九、主函数

不管一个程序中有多少个函数,但主函数(也就是main函数)只能有一个,并且main函数是整个程序的入口,也是整个程序的出口,主函数可以调用任何其他函数,其他函数之间也可以互相调用,但是其他函数不能调用主函数。

十、函数使用举例

定义6个整型变量a,b,c,d,e,f,并对它们任意赋值。分别输出a,b的最大值,c,d的最大值和e,f的最大值

要求:自定义函数并且不能与引例中的函数执行体相同

#include <stdio.h>

int get_max(int i, int j)
{
    if (i > j)
        return i;
    else if (i < j)
        return j;
}

int main()
{
    int a, b, c, d, e, f;

    a = 5, b = 3, c = 6, d = 87, e = 12, f = 49;

    printf("%d\n", get_max(a, b));
    printf("%d\n", get_max(c, d));
    printf("%d\n", get_max(e, f));

    return 0;
}

输入三个int类型的数字,并判断这三个数字是否是素数。如果是素数输出Yes,如果不是输出No

要求:使用自定义函数实现

提示:素数(又称质数)是大于1的自然数并且素数只能被1和它本身整除

#include <stdio.h>

int is_prime(int val)
{
	int i;

	for (i = 2; i < val; i++)
		if (val % i == 0)
			break;

	if (i == val)
		return 1;
	else
		return 0;
}

int main()
{
	int val1, val2, val3, i;

	scanf("%d%d%d", &val1, &val2, &val3);

	if ( is_prime(val1) )
		printf("Yes!\n");
	else
		printf("No!\n");

	if ( is_prime(val2) )
		printf("Yes!\n");
	else
		printf("No!\n");

	if ( is_prime(val3) )
		printf("Yes!\n");
	else
		printf("No!\n");

	return 0;
}

十一、函数的声明

观察可以发现,文章中所有自定义的函数都放在了main函数之上,那我们如果将自定义的函数放在main函数的下面会出现什么情况呢?如果我们在main函数中调用了自定义函数,并且将自定义的函数放在了main函数的下方,此时整个程序就会编译报错,这是因为main函数执行到调用自定义函数的语句时,它只会向上查找对应的函数,而如果在它上面没有对应的函数,程序就会出错。

思考1:下面两个程序是否可以运行?

#include <stdio.h>

int main()
{
	f();

	return 0;
}

void f()
{	
	printf("AAAA\n");
}

那如果我们要把被调函数放在主调函数之后,我们应该怎么办呢?这需要在主调函数的上方加上函数声明即可,如下程序所示:

#include <stdio.h>

void f(); // 这是函数声明,后面的分号不可以省略

int main()
{
	f();

	return 0;
}

void f()
{	
	printf("AAAA\n");
}

思考2:下面的程序是否可以运行?如果有错误应该如何改正?

#include <stdio.h>

void g(void)
{
	f();
}

void f(void)
{
	printf("AAAA\n");
}

int main()
{
	g();

	return 0;
}

十二、函数的形参与实参

形参是指定义函数时括号中定义的变量,而实参是指在调用函数时向被调函数传输的具体的数据或变量。如下代码所示,变量i就是形参,而主函数中调用f函数时在后面的括号中写的5就是实参。需要注意的是,形参和实参的个数必须是对应的,数据类型也必须相互兼容。

#include <stdio.h>

void f(int i)
{	
	printf("%d\n", i);
}

int main()
{
	f(5);

	return 0;
}

十三、合理设计函数

在掌握了以上知识之后,我们该如何设计函数让整个程序更像是开发软件?答案很简单,如果仅需要使用一次某个功能,则不需要将该功能设计成一个函数,但如果需要多次使用该功能,则设计一个该功能对应的函数是不二选择。这段话通过以下三个程序体会:

示例:输入一个int类型的数字n,求1到n之间(包括n)所有的素数并输出。不考虑输入的值小于等于1的情况

解法1:不定义其它函数,仅在main函数中实现

#include <stdio.h>

int main()
{
	int n;
	int i, j;

	scanf("%d", &n);

	for (i = 2; i <= n; i++)
	{
		for (j = 2; j < i; j++)
			if (i % j == 0)
				break;
		
		if (j == i)
			printf("%d\n", i);
	}

	return 0;
}

这样写程序虽然实现了功能,但代码的重用性非常低,比如,我们要分别判断100个不同数字从1到它本身之间的所有素数,则需要将下面的代码块重复写100次,这样就会导致代码大部分是重复的,而且代码量会显得非常大。

for (i = 2; i <= n; i++)
{
	for (j = 2; j < i; j++)
		if (i % j == 0)
			break;
	
	if (j == i)
		printf("%d\n", i);
}

考虑到要将1到n之间所有的数字都进行判断素数这一操作,所以可以单独设计出一个函数实现判断素数这个功能,从而解决代码重复的问题。

解法2:自定义一个函数实现

#include <stdio.h>

int is_prime(int val)
{
	int i;

	for (i = 2; i < val; i++)
		if (val % i == 0)
			break;

	if (i == val)
		return 1;
	else
		return 0;
}

int main()
{
	int n;
	int i;

	scanf("%d", &n);

	for (i = 2; i <= n; i++)
		if ( is_prime(i) )
			printf("%d\n", i);

	return 0;
}

相比较第一个程序,该程序更容易让人理解,代码的可重用性也得以提高,并且多次判断时所需要写的代码也相对较少,但存在的问题与第一个程序相同,如果要分别判断100个不同数字从1到它本身之间的所有素数,则需要将下面的代码块重复写100次。

for (i = 2; i <= n; i++)
    if ( is_prime(i) )
        printf("%d\n", i);

又考虑到判断完是否是素数后,输出也是重复性操作,所以同样可以将输出这一操作通过设计函数来实现。

解法3:自定义两个函数实现

#include <stdio.h>

// 本函数的功能是判断val是否是素数
int is_prime(int val)
{
	int i;

	for (i = 2; i < val; i++)
		if (val % i == 0)
			break;

	if (i == val)
		return 1;
	else
		return 0;
}

// 本函数的功能是将1到n之间(包括n)所有的素数输出
void traverse(int n)
{
	int i;

	for (i = 2; i <= n; i++)
		if ( is_prime(i) )
			printf("%d\n", i);
}

int main()
{
	int n;
	int i;

	scanf("%d", &n);
	traverse(n);

	return 0;
}

第三个程序相较前两个程序而言,代码量更少,且可重用性更高,如果需要判断多个数字并输出的话,只需要多次调用traverse函数即可实现功能,而且整个程序更容易让人理解。

总结:一个函数的功能尽可能独立单一,不要将多个功能放在一个函数中实现,这样不仅可以避免代码的重复而且还可以增加整个程序的可重用性与可读性。

十四、变量的作用域

变量按作用域划分可以分为全局变量和局部变量。

全局变量

在所有函数外部定义的变量被称为全局变量。如下程序所示,变量k就是一个全局变量,需要注意的是,全局变量只能在定义全局变量的位置之后的函数中使用,在全局变量之上的函数不能使用。

#include <stdio.h>

int k = 10;

void f()
{
	printf("%d\n", k);
}

int main()
{
	f();
	
	return 0;
}

局部变量

在函数内部定义的变量,以及函数的形参都属于局部变量。如下程序所示,f函数中的形参i以及f函数中定义的变量j都是f函数的局部变量,main函数中定义的变量i也是main函数的局部变量。除此之外,初学时可能会有这样的困惑:f函数中定义了i变量,main函数中也定义了i变量,这样程序是否存在问题?答案是不存在问题的,因为main函数中变量i只在main函数中发挥作用,而f函数中的变量i只在f函数中发挥作用,所以它们并不会起冲突,也就意味着,所有的局部变量只能在函数的内部使用,这也正是局部变量的含义。

#include <stdio.h>

void f(int i)
{
	int j = 20;
}

int main()
{
	int i = 10;

	return 0;
}

全局变量名与局部变量名冲突的问题

#include <stdio.h>

int i = 99;

void f(int i)
{
	printf("i = %d\n", i);
}

int main()
{	
	f(8);

	return 0;
}

推测1:程序中有错误,因为全局变量名与局部变量名一样,无法分辨f函数中到底是全局变量i还是局部变量i

推测2:程序无误,如果输出结果为99,则此时的变量i是全局变量;如果输出结果为8,则此时的变量i是局部变量

结果:输出结果为8

结论:如果全局变量名与局部变量名相同,则局部变量会屏蔽全局变量的存在

十五、函数内存的分配

在使用函数之前,操作系统会给函数中所有的变量分配内存空间,但当函数被执行完毕后,其中所有变量的内存均会被释放掉,等到再次使用时,会重新给函数中的变量分配内存空间。大家可以思考一下这样做的合理性。

以上就是C语言学习之函数知识总结的详细内容,更多关于C语言 函数 的资料请关注猪先飞其它相关文章!

原文出处:https://blog.csdn.net/weixin_65334260/article/details/125435

[!--infotagslink--]

相关文章

  • C语言实现放烟花的程序

    这篇文章主要为大家详细介绍了C语言实现放烟花的程序,有音乐播放,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-23
  • C语言中的字符(char)详细讲解

    本篇文章主要介绍C语言中char的知识,并附有代码实例,以便大家在学习的时候更好的理解,有需要的可以看一下...2020-04-25
  • php正确禁用eval函数与误区介绍

    eval函数在php中是一个函数并不是系统组件函数,我们在php.ini中的disable_functions是无法禁止它的,因这他不是一个php_function哦。 eval()针对php安全来说具有很...2016-11-25
  • php中eval()函数操作数组的方法

    在php中eval是一个函数并且不能直接禁用了,但eval函数又相当的危险了经常会出现一些问题了,今天我们就一起来看看eval函数对数组的操作 例子, <?php $data="array...2016-11-25
  • Python astype(np.float)函数使用方法解析

    这篇文章主要介绍了Python astype(np.float)函数使用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-06-08
  • Python中的imread()函数用法说明

    这篇文章主要介绍了Python中的imread()函数用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-16
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • 详解如何将c语言文件打包成exe可执行程序

    这篇文章主要介绍了详解如何将c语言文件打包成exe可执行程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-25
  • 金额阿拉伯数字转换为中文的自定义函数

    CREATE FUNCTION ChangeBigSmall (@ChangeMoney money) RETURNS VarChar(100) AS BEGIN Declare @String1 char(20) Declare @String2 char...2016-11-25
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • C++中 Sort函数详细解析

    这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
  • PHP用strstr()函数阻止垃圾评论(通过判断a标记)

    strstr() 函数搜索一个字符串在另一个字符串中的第一次出现。该函数返回字符串的其余部分(从匹配点)。如果未找到所搜索的字符串,则返回 false。语法:strstr(string,search)参数string,必需。规定被搜索的字符串。 参数sea...2013-10-04
  • PHP函数分享之curl方式取得数据、模拟登陆、POST数据

    废话不多说直接上代码复制代码 代码如下:/********************** curl 系列 ***********************///直接通过curl方式取得数据(包含POST、HEADER等)/* * $url: 如果非数组,则为http;如是数组,则为https * $header:...2014-06-07
  • php中的foreach函数的2种用法

    Foreach 函数(PHP4/PHP5)foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。...2013-09-28
  • C语言中free函数的使用详解

    free函数是释放之前某一次malloc函数申请的空间,而且只是释放空间,并不改变指针的值。下面我们就来详细探讨下...2020-04-25
  • C语言中计算正弦的相关函数总结

    这篇文章主要介绍了C语言中计算正弦的相关函数总结,包括正弦和双曲线正弦以及反正弦的函数,需要的朋友可以参考下...2020-04-25
  • 详解C语言中的rename()函数和remove()函数的使用方法

    这篇文章主要介绍了详解C语言中的rename()函数和remove()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25
  • PHP函数strip_tags的一个bug浅析

    PHP 函数 strip_tags 提供了从字符串中去除 HTML 和 PHP 标记的功能,该函数尝试返回给定的字符串 str 去除空字符、HTML 和 PHP 标记后的结果。由于 strip_tags() 无法实际验证 HTML,不完整或者破损标签将导致更多的数...2014-05-31
  • SQL Server中row_number函数的常见用法示例详解

    这篇文章主要给大家介绍了关于SQL Server中row_number函数的常见用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-12-08