函数探秘:深入理解C语言函数,实现高效模块化编程

这篇具有很好参考价值的文章主要介绍了函数探秘:深入理解C语言函数,实现高效模块化编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

函数探秘:深入理解C语言函数,实现高效模块化编程

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C语言学习
贝蒂的主页:Betty‘s blog

1. 函数的概念

在数学中我们就知道了函数这个概念,而C语言同样引入了函数这个概念,那C语言的函数到底是什么样的呢?

在C语言中,函数也叫子程序,它是一段可以重复使用的代码,用来独立地完成某个功能,它可以接收用户传递的数据,也可以不接收。

2. 函数的分类

C语⾔的程序其实是由⽆数个⼩的函数组合⽽成的,也可以说:⼀个⼤的计算任务可以分解成若⼲个较⼩的函数(对应较⼩的任务)完成。同时⼀个函数如果能完成某项特定任务的话,这个函数也是可以复⽤的,提升了开发软件的效率。而C语言函数大致可以分为两类:库函数自定义函数

2.1 库函数

C语⾔标准中规定了C语⾔的各种语法规则,但是C语⾔并不提供库函数;C语⾔的国际标准ANSIC规定了⼀些常⽤的函数的标准,被称为标准库,那不同的编译器⼚商根据ANSI提供的C语⾔标准就给出了⼀系列函数的实现。这些函数就被称为库函数

比如说我们前面学习的printfscanf都是库函数,它的头文件时<stdio.h>。但是我们今天的主题不是讲解库函数,如果大家想学习其他库函数,可以去C/C++官方学习

函数探秘:深入理解C语言函数,实现高效模块化编程

2.2 自定义函数

自定义函数是由程序员自主设计的函数,和库函数一样有函数名、返回类型、形式参数等,今天我们的目标就是学习如何写自定义函数。

3. 自定义函数

3.1 语法

dataType functionName(形式参数)

{
//body
}

  • dataType 是返回值类型,它可以是C语言中的任意数据类型,例如 int、float、char 等。
  • functionName 是函数名,它是标识符的一种,命名规则和标识符相同。函数名后面的括号( )不能少。
  • body 是函数体,它是函数需要执行的代码,是函数的主体部分。即使只有一个语句,函数体也要由{ }包围。
  • 如果有返回值,在函数体中使用 return 语句返回。return 出来的数据的类型要和 dataType 一样。

3.2 实例

通过函数的定义我们可以写一个简单的加法函数:

#include<stdio.h>
int Add(int x, int y)
//函数名Add
//两个形式参数x,y,类型都是int
//返回类型也是int
{
	int c = 0;
	c = x + y;
	return c;//返回x+y的和
}
int main()
{
	int a, b;
	scanf("%d%d", &a, &b);
	int ret = Add(a, b);//加法函数
	printf("两个数和为%d\n", ret);
	return 0;
}

当然我们也可以优化一下

int Add(int x, int y)
{
	return x + y;//直接返回
}
  • 通过函数我们就可以节省大量时间,每次调用这个功能只需复用该函数即可。

3.3 作用域与生命周期

作用域生命周期是C语言中一个特别重要的概念,清楚理解这个概念能帮助我们写出更好的程序,减少bug的产生。

作⽤域(scope)是程序设计概念,通常来说,⼀段程序代码中所⽤到的名字并不总是有效(可⽤的,⽽限定这个名字的可⽤性的代码范围就是这个名字的作⽤域。

  1. 局部变量的作⽤域是变量所在的局部范围。
  2. 全局变量的作⽤域是整个⼯程(项⽬)。
  • 以上面加法函数以列,x,y的作用域就是加法函数。当加法函数执行结束,它就会被销毁。

⽣命周期指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的⼀个时间段。

  1. 局部变量的⽣命周期是:进⼊作⽤域变量创建,⽣命周期开始,出作⽤域⽣命周期结束。

  2. 全局变量的⽣命周期是:整个程序的⽣命周期。

  • 以上面加法函数以列,x,y的生命周期就是其进入函数创建到变量销毁的时间段。

4. 函数的参数

在函数使⽤的过程中,把函数的参数分为,实际参数形式参数。下面我仍将以加法函数举例:

#include<stdio.h>
int Add(int x, int y)
//函数名Add
//两个形式参数x,y,类型都是int
//返回类型也是int
{
	int c = 0;
	c = x + y;
	return c;//返回x+y的和
}
int main()
{
	int a, b;
	scanf("%d%d", &a, &b);
	int ret = Add(a, b);//加法函数
	printf("两个数和为%d\n", ret);
	return 0;
}

4.1 实际参数

顾名思义,实际参数就是实际存在的参数,也就是主函数main调用函数时传给它的参数,也就是上面主函数中的a,b。

4.2 形式参数

同理,形式参数其实就是形式上的参数,也就是上面加法函数Add中的x,y。它并不是实际存在的,在未使用时并不会向内存申请空间,形式参数只有在函数被调⽤的过程中为了存放实参传递过来的值,才向内存申请空间,这个过程就是形式的实例化。

4.3 实参与形参的关系

在介绍实参和形参关系时,我们先看看下面这段代码

#include<stdio.h>
void swap(int x, int y)//返回类型为void表示不返回值
{
    int temp = 0;//定义一个临时变量
    temp = x;//把x的值赋给temp
    x = y;//把y的值赋给x
    y = temp;//把temp的值赋给y,完成交换操作
}
int main()
{
    int a = 0;
    int b = 0;
    scanf("%d %d", &a, &b);
    printf("交换前:a=%d,b=%d\n", a, b);
    swap(a, b);//交换函数
    printf("交换后:a=%d,b=%d\n", a, b);
    return 0;
}

那么交换后的输出是多少呢~

函数探秘:深入理解C语言函数,实现高效模块化编程

为什么交换的值还是不变呢,我们可以通过调试来观察一下

函数探秘:深入理解C语言函数,实现高效模块化编程

通过调试,我们发现a与x,b与y只是在数值上相同,他们地址是完全不同的,他们在两个独立的内存空间互不影响,对x,y交换根本不会改变a,b的值。并且函数在调用完成之后,函数所申请的内存空间会归还系统~

所以我们总结实参与形参的关系是:形参只是实参的⼀份临时拷⻉,对形参改变根本不会影响实参

4.4 数组做参数

我们前面学过的数组自然是也能作为函数的参数来使用,下面是一个具体实例:

void print_arr(int arr[], int sz)//打印数组
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
  • 其中arr是整个数组,sz是数组元素的个数

5. 嵌套调用和链式访问

5.1 函数的嵌套调用

我们在前面已经学习了选择结构和循环结构的嵌套使用,那函数嵌套调用又是怎样的呢。其实,简单来说,就是函数中也可以调用函数

举个例子,让我们看看下面这段代码

void print2()
{
	printf("hello world\n");
}
void print1()
{
	print2();//调用print2函数
}
int main()
{
	print1();//调用print1函数
         return 0;
}

函数探秘:深入理解C语言函数,实现高效模块化编程

  • C语言函数支持嵌套调用,但是不支持嵌套定义。

5.2 函数的链式访问

链式访问简单来说就是将⼀个函数的返回值作为另外⼀个函数的参数,像链条⼀样将函数串起来就是函数的链式访问。

举例:

这是strlen的函数声明:size_t strlen(const char *str)

#include <stdio.h>
int main()
{
	printf("%d\n", strlen("abcdef"));
    //根据strlen的返回值,我们就可以直接将其作为printf的参数
    //这就是一种简单的链式访问
	return 0;
}

6.函数的定义与声明

6.1 函数的定义

函数的定义就是指函数具体的实现过程,交代函数具体功能的实现

比如我们前面写的加法函数具体实现的过程就是函数的定义

int Add(int a, int b)
{
	return a + b;//直接返回
}

但是特别注意函数不能嵌套定义

int add(int x, int y)//加法函数
{
    return x + y;
    void print()
    {
        ....;//嵌套定义error
    }
}

6.2 函数的声明

不知道大家注意到一个细节没有,就是贝蒂在写函数时候,会把函数放在主函数之前,那如果放在主函数之后会怎么样呢,让我们实验一下吧

int main()
{
	int a, b;
	scanf("%d%d",&a,&b);
	int ret = Add(a, b);
	printf("%d", ret);
	return 0;
}
int Add(int x, int y)
{
	return x + y;
}

函数探秘:深入理解C语言函数,实现高效模块化编程

一运行就会报这个警告,这个警告的意思就是说Add函数未定义,那为什么说Add未定义呢,原来是程序从上往下进行的,在遇到Add函数之前,会先执行主函数中的Add,此时程序根本不知道Add的含义,所以会报警告。

那如何消除这个警告呢,其实我只需要加一个声明,让程序提前知道有这个函数。

int Add(int x, int y);//函数声明
int main()
{
	int a, b;
	scanf("%d%d",&a,&b);
	int ret = Add(a, b);
	printf("%d", ret);
	return 0;
}
int Add(int x, int y)
{
	return x + y;
}
int Add(int , int );//声明写出这样也是可以的
  • 函数的调⽤⼀定要满足,先声明后使⽤是,而函数的定义也是⼀种特殊的声明,所以如果函数定义放在调⽤之前也是可以的。

7. 函数的迭代与递归

7.1 函数的迭代

「迭代 iteration」是一种重复执行某个任务的控制结构。在迭代中,程序会在满足一定的条件下重复执行某段代码,直到这个条件不再满足。我们学习过的while,for,do-while循环结构就是一种迭代。

7.2 函数的递归

「递归 recursion」是一种算法策略,通过函数调用自身来解决问题。它主要包含两个阶段。

  1. 递:程序不断深入地调用自身,通常传入更小或更简化的参数,直到达到“终止条件”。
  2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。

递归的三个要素:

  1. 终止条件:用于决定什么时候由“递”转“归”。

  2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。

  3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。

7.3 递归问题

(1) 问题一

利用递归计算1+2+3+4+....+n

int recur(int n)
{
	// 终止条件
	if (n == 1)
	{
		return 1;
	}
	// 递:递归调用
	return n + recur(n - 1);
}

函数探秘:深入理解C语言函数,实现高效模块化编程

(2) 问题二

输⼊⼀个整数m,打印这个按照顺序打印整数的每⼀位。
⽐如:
输⼊:1234 输出:1 2 3 4
输⼊:520 输出:5 2 0

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

代码分析:文章来源地址https://www.toymoban.com/news/detail-837847.html

void print(int n)//输入1234
{
    if (n > 9)//1234大于9
    {
        print(n / 10);//将123传入下个递归
    }
    printf("%d ", n % 10);//归的第四步,输出4
}
void print(int n)//传入123
{
    if (n > 9)//123大于9
    {
        print(n / 10);//将12传入下个递归
    }
    printf("%d ", n % 10);//归的第二步,输出3
}
void print(int n)//传入12
{
    if (n > 9)//12大于9
    {
        print(n / 10);//将1传入下个递归
    }
    printf("%d ", n % 10);//归的第一步,输出2
}
void print(int n)//传入1
{
    if (n > 9)//1不大于9
    {
        print(n / 10);
    }
    printf("%d ", n % 10);//输出1,开始‘归’
}

到了这里,关于函数探秘:深入理解C语言函数,实现高效模块化编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Node.js-模块化理解及基本使用

    讲一个复杂的程序文件按照一定的规则拆分成多个独立的小文件,这些小文件就是小模块,这就是模块化。 每个小模块内部的数据是私有的,可以暴露内部数据给外部其他模块使用。 减少命名的冲突 提高复用性 提高可维护性 按需加载 1.单个模块暴露 2.多个模块暴露 暴露数

    2024年02月14日
    浏览(45)
  • 玩转C语言:深入理解输入输出函数的奥秘!

    ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C语言学习 贝蒂的主页:Betty‘s blog 在C语言中有一个函数putchar专门负责输出单个字符,其语法如下: 头文件:#includestdio.h 声明:int putchar(int char) char  – 这是要被写入的字符。该字符以其

    2024年02月22日
    浏览(43)
  • 玩转C语言:深入理解输入输出函数的奥秘

    ​ ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C语言学习 贝蒂的主页:Betty‘s blog 在C语言中有一个函数putchar专门负责输出单个字符,其语法如下: 头文件:#includestdio.h 声明:int putchar(int char) char -- 这是要被写入的字符。该字符以其

    2024年02月21日
    浏览(49)
  • js模块化CJS、AMD、CMD、UMD、ESM、IIFE理解

            JavaScript模块化编程是指将JavaScript代码分割成独立的模块,每个模块都有自己的作用域和接口,可以按需加载和使用。这样可以避免全局变量污染,提高代码的可维护性和可重用性。在ES6之前,JavaScript并没有一个统一的模块化规范,开发人员通常使用CommonJS或AMD等

    2024年02月10日
    浏览(41)
  • Go指针探秘:深入理解内存与安全性

    Go指针为程序员提供了对内存的深入管理能力,同时确保了代码的安全性。本文深入探讨了Go指针的基础概念、操作、深层理解及其特性与限制。通过深入了解其设计哲学和应用,我们可以更好地利用Go的强大功能。 关注公众号【TechLeadCloud】,分享互联网架构、云服务技术的

    2024年02月08日
    浏览(45)
  • 【前端JS交互篇】函数、参数、返回值、闭包函数、递归函数、内存、模块化编程

    函数可以封装一些功能,可以供外部去重复的调用。所以,一般我们把函数叫做具有重复功能的代码块。 JavaScript 基础到高级 Canvas游戏开发 原生JavaScipt案例合集 JavaScript +DOM基础 假设饭店就是一个函数,饭店的功能就是做各种各样的菜,但是具体做什么菜,需要用户来点,用

    2024年02月15日
    浏览(54)
  • Dubbo 模块探秘:深入了解每个组件的独特功能【二】

    欢迎来到我的博客,代码的世界里,每一行都是一个故事 在 Dubbo 的分布式舞台上,每个模块都是为了呈现最完美的表演。今天,我们将深入 Dubbo 的模块体系,解析每个组件的独特功能,为你揭开构建分布式服务的神秘面纱。让我们一同踏上这场 Dubbo 模块之旅,探寻分布式服

    2024年01月22日
    浏览(50)
  • C语言模块化编程思维

    eg1:代码案例演示 1:程序的头文件 2:程序逻辑函数文件(这里使用一个延时函数为例子) 3:程序的主文件控制函数的实现 (main函数是c语言程序的入口一个c文件有且仅仅只能有一个main函数文件) 以上是编写代码的一种思路,要进一步掌握需要在实际的编程中锻炼 c语言模

    2024年01月21日
    浏览(40)
  • 《深入理解Java虚拟机》读书笔记:HotSpot虚拟机对象探秘

    基于实用优先的原则,以常用的虚拟机HotSpot和常用的内存区域Java堆为例,深入探讨HotSpot虚拟机在Java堆中对象分配、布局和访问的全过程。以下是本节内容的脑图。   HotSpot虚拟机对象探秘脑图   创建对象大致分为5步:1.检查类是否加载,没有加载先加载类 2.分配内存 3.初始

    2024年02月14日
    浏览(65)
  • c语言练习41:深入理解字符串函数strlen strcpy strcat

    模拟实现:”strlen   strcpy   strcat strlen   strcat:

    2024年02月09日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包