函数(2)

本文详细介绍了C语言中的函数使用,包括函数的嵌套调用、链式访问、递归以及函数的声明和定义。通过示例代码解释了嵌套调用如何在一个函数内部调用其他函数,链式访问如何将函数返回值作为另一个函数参数。同时,文章讲解了函数递归的概念,提供了打印字符串每位数字和计算字符串长度的递归函数示例。此外,还探讨了函数声明与定义的区别,强调了在实际开发中头文件和源文件的组织方式。
摘要由CSDN通过智能技术生成

目录

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

   5.1  嵌套调用

   5.2  链式访问

6.  函数的声明和定义

  6.1  函数声明

  6.2  函数定义

7.  函数递归

  7.1  什么是递归?

  7.2  递归的两个必要条件


在讲之前再讲一下代码要规范:如下

//代码1

#include<stdio.h>
test()
{
	printf("hehe\n");
}
int main()
{
	int ret=test();
	printf("%d\n", ret);
	return 0;
}

我们可以看出test是有返回值的。

(1)C语言规定:函数的返回类型不写的时候,默认是返回的是int类型。

建议:真的不需要函数返回值的时候,明确写void。

(2)为什么返回的是5呢?(我们现在知道就行,不用深入了解)

①除main外,其余函数没有写return,取决于编译器的实现(返回的一般,是函数执行完的最后一条指令所产生的结果)

②main,没写return,默认返回0 

(3)printf函数的返回值是打印在屏幕上字符的个数。

//代码2
#include<stdio.h>
void test()
{
	printf("hehe\n");
}
int main()
{
	test(100);
	return 0;
}

如上:虽可运行,但不严谨

C的不严谨:不需要传参时,应写void;否则完全可能传参,只是函数没有接收。


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

函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的

5.1  嵌套调用

函数的嵌套调用,即在一个被调函数内部,又调用了其他的函数。

1、代码演示:

//嵌套调用
#include<stdio.h>

void new_line(void)
{
	printf("hehe\n");
}

void three_line(void)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		new_line();//调用new_line三次
	}
}

int main()
{
	three_line();//调用three_line
	return 0;
}

2、图示如下:

 注:函数可以嵌套调用,但是不能嵌套定义

5.2  链式访问

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

例子:

//代码1

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[20] = "hello";
	int ret = strlen(arr);//计算字符串长度,注统计‘\0’之前的字符个数
	printf("%d\n", ret);
	printf("%d\n", strlen(arr));//strlen的返回值作为printf的参数
	return 0;
}

如上,我们应该能体会到什么是链式访问了。 

我们再举一个链式访问的经典例子:

//代码2

#include<stdio.h>
int main()
{
	printf("%d", printf("%d", printf("%d", 43)));
	//printf的返回值是打印在屏幕上字符的个数
	return 0;
}

tip:printf函数的返回值是打印在屏幕上字符的个数。

程序输出:4321

6.  函数的声明和定义

6.1  函数的声明

1、告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。(声明只是告诉你有,但是真的有没有取决于定义)

声明的一般形式如下:

           ①返回类型   函数名(形参列表); 如:int  Add(int x,int y);

           ②返回类型   函数名(形参类型列表); 如:int  Add(int ,int );

好的风格:建议使用①;  

注:函数的声明以“;”结束,不能省略。

2、函数的声明一般出现在函数的使用之前,要满足先声明后使用。

3、函数的声明一般放在头文件中的

6.2  函数的定义

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

说明:函数为什么要声明?

代码在编译的时候,要进行代码的扫描,是顺序从前往后扫描的。

1、当函数定义出现在函数调用之后时,在主调函数前,采用函数原型对被调用函数进行声明。

当不声明,编译器会警告,如下:

2、函数定义出现在函数调用之前,可不用进行函数声明。(函数的定义也是一种特殊的声明)

#include<stdio.h>
//函数的声明
int Add(int x, int y);

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

//函数的定义
int Add(int x, int y)
{
	return x + y;
}

这就学会了函数的定义和声明吗?

函数的声明和定义,不是这样用的,以上只是它的一种运用场景,只是它的语法展示,真正在工程中,函数的定义和声明,我们是怎么写的呢?

头文件:.h——放置函数的声明

源文件:.c——放置函数的实现(定义)

如要写一个函数实现求两个整数的和:

1、创建一个源文件:add.c——放置函数的实现(定义)

 2、创建一个头文件:add.h——放置函数的声明

3、测试——再创建一个源文件:test.c 

在工程中想要使用(调用)Add函数,也是如库函数一样要打招呼。

库函数:使用库函数,必须包含 #include 对应的头文件。

自定义函数:也如库一样,必须包含#include对应的头文件。

 如上截图:库函数的头文件名一般用<>引用,自定义函数的头文件名一般用“”引用。

我们本可以在一个.c文件写的,为什么分成三个文件呢?好处是什么呢?

1、模块化开发(分工)

例:要实现一个计算机

       加法——a(程序员做)——add.c  add.h

       减法——b——sub.c  sub.h

       乘法——c——mul.c  mul.h

       除法——d——div.c   div.h

如果不分开,模式化开发,在一个.c文件a、b、c、d四个人怎么同时写了?,还会使代码变得混乱。  

分开,模式化开发每个人都可以同时写,写完后再组成一个.c文件就完成了计算机了。

2、代码的隐藏(可以使用,但不知道源代码)

(1)具体步骤:项目名——属性——配置属性——常规——配置类型——改成静态库——应用——确定——ctrl+f7——在文件中生成xx.lib文件——用记事本查看——显示乱码

(2)在另一个项目使用:需要导入xx.lib和xx.h文件

       还需要在主调函数前:#pragma  comment(lib,"xx.lib"),就可使用了

现在知道不再一个.c文件写有隐藏代码的好处就行了,不用深入理解,后期我会在博客中写的。

总结:

函数的声明我们一般放置在头文件中

函数的实现我们都是放置在源文件中

7.  函数的递归

7.1  什么是递归

     程序调用自身的编码技巧称为递归(recursion)。

     递归做为一种算法在程序设计语言中广泛应用。一个函数(或过程)在其定义(或说明)中 有直接或间接调用自身 的一种方法。
     它通常把一个 大型复杂 的问题 层层转化 为一个与原问题 相似 的规模较小的问题来求解。
     递归策略 只需少量 的程序就可 描述出解题过程 所需要的 多次重复计算 ,大大地 减少 了程序的代码量。
     递归的主要思考方式在于:把大事化小
最简单的递归:
#include<stdio.h>
int main()
{
	printf("hehe\n");
	main();//递归:在函数内自己调用自己
	return 0;
}

f10调试:

 7.2  递归的两个必要条件

1、存在限制条件,当满足这个限制条件的时候,递归便不继续(递归出口)

2、每次递归调用之后越来越接近这个限制条件

7.2.1  练习1

接受一个整型值(无符号),按照顺序打印它的每一位。
例如:
输入: 1234 ,输出1 2 3 4
void print(unsigned int n)
{
	if (n > 9)//限制条件
	{
		print(n / 10);//递归调整,接近限制条件
	}
	printf("%d ", n % 10);
}

int main()
{
	unsigned int num = 0;
	scanf("%d", &num);//1234
	//写一个函数打印num的每一位
	print(num);
	return 0;
}

递归的理解

递——递推

归——回归

先递推后回归。

7.2.2  练习2

编写函数不允许创建临时变量,求字符串的长度。
#include<stdio.h>

int my_strlen(char* str)
{
	if (*str != '\0')
	{
		return 1 + my_strlen(str + 1);//str+1,字符指针+1,向后跳一个字符
	}
	else
	{
		return 0;
	}
}
int main()
{
	char arr[] = "bit";
	int ret = my_strlen(arr);//数组名其实是首元素地址
	printf("%d\n", ret);
	return 0;
}

(1)数组名其实是首元素的地址

(2)字符指针+1,向后跳一个字符。(那整形指针+1,向后跳四个字符)

 总结:递归一般都要有两个必要条件(一般与if搭配)

1、有限制条件

2、每一次递推越来越接近限制条件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值