大家好,又见面了这里是笨鸟先飞,哈哈哈!这次为大家分享函数的进阶理解。
目录
函数的分类
C语言中函数分为库函数和自定义函数。
库函数就是我们平常使用的一些函数如(printf)输出函数,(strcpy)拷贝函数对字符串进行拷贝,(pow)是计算n的k次方这样的运算。这样的函数还有很多C语言将这些常用的函数存放在库中,所以我们称他们为库函数。而我们要调用库函数时,必须要引用相应的头文件。
库函数
在C语言中常见的库函数大概可以分为这几类:
- IO函数
- 字符串操作函数
- 字符操作函数
- 内存操作函数
- 时间/日期函数
- 数学函数
- 其他库函数
在我们具体学习库函数时,可以从这个cplusplus.com - The C++ Resources Network,cppreference.com网址上去学习。这就是对库函数的简单介绍了,函数中最重要的还是自定义函数,如果库函数就可以把所有事情都完成了,那要我们程序猿有什么用呢!
自定义函数
自定义函数 顾名思义就是我们自己定义函数,自己设计函数这给了我们很大自由发挥空间。
int Add(char n)
{
//语句项
}
//int 函数值的返回类型
//Add 函数名
//n 函数参数
//char 接收函数参数的类型
简单介绍一个函数的组成部分。接下介绍函数的参数部分,函数参数部分可以分为:
1.形式参数(形参)
形式参数是指函数名括号中的变量,因为形式参数只有被调用的时候才创建内存单元,所以叫做形式参数。形参在函数调用完成后就自动销毁,使用时创建。因此形参只在函数内部有用。
2.实际参数(实参)
真实传递给函数的参数叫做实参,实参可以是常量,变量,表达式,函数等。无论实参是何种类型的变量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
下面是用画图的方法让大家进一步理解实参和形参
通过上述画图理解,大家可以把形参理解为实参的一份临时拷贝。形参的地址是独立开辟的与实参不同。
函数的调用
函数可以分为两种一种叫传值调用,一种叫做传址调用。
传值调用
函数的形参和实参分别占用不同的内存块也就是地址不同,对函数形参的修改不能影响实参。
传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
下面通过一个习题为大家让大家理解传值调用与传址调用。的区别。
通过一个函数实现两个数值的交换。
运行的结果是这样的。
可以发现这里a和b的值并没有交换,因为这里是传值调用,传值过去后形参会重新开辟一个地址,改变形参的值影响不了实参的值。形参只是实参的一份临时拷贝。
接下来我们用传址调用来试试这个题。
可以看到只有通过传址调用,a和b的值才能进行交换,因为传址调用通过传地址过去,用指针接收时,与主函数搭建了联系了,通过解引用*px的操作找到了a,并且将他的值进行交换。
总结:传值调用时,无法改变通过修改形参的值去改变实参的值,当涉及到实参的值需要改变时我们需要使用传址调用。传址调用时他的地址没有发生改变,只是可以改变地址里面存放的值。
函数的嵌套调用和链式访问
嵌套调用
函数是可以嵌套调用的,但不能嵌套定义,也就是在函数内部定义的只能在内部使用,外部无法使用。嵌套调用的话举例子为大家说明。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void cozxyt()
{
printf("hehe\n");
}
void test()
{
printf("hehe\n");
cozxyt();
}
int main()
{
test();
return 0;
}
下面是运行结果
可以看到函数通过调用打印了两个hehe。
链式访问
链式访问的定义是把一个函数的返回值作为另外一个函数的参数。
int main()
{
char arr[] = {"abcdef"};
int ret = strlen(arr);//求字符串长度
printf("%d\n", ret);
printf("%d\n", strlen(arr));//将strlen(arr)作为函数返回值的参数。
return 0;
}
可以看到这里是将strlen(arr)当作参数,他和输出ret的值结果其实是一样的。下面给大家放运行截图。
可以看到字符串的长度都为6,第二个输出就是通过链式访问去输出。
下面给大家整个经典的链式访问的题目
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
}
//请问结果是什么?
//注明printf的返回值是打印在屏幕上字符的个数
我想问问大家这个题目最终的结果是什么,大家可以根据下面的提示想一想printf的返回值是打印在屏幕上字符的个数。答案是什么呢下面我们运行一下看看。
可以看到这里运行的结果是4321,这就是经典的套娃。printf的返回值是打印在屏幕上字符的个数
最后一个printf打印出43,第二个printf看到打印在屏幕上的是两个字符就输出2,第一个printf看到2是一个字符就输出1。
函数的声明与定义
函数声明:
1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数 声明决定不了。
2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
3. 函数的声明一般要放在头文件中的。
//函数的声明
int Add(int x,int y);
函数的声明只需要告诉我们函数的返回类型,函数的名字,参数的类型,这里的X,Y其实是可有可无的。我们的编译器执行时是从上往下扫描,我们国内学校很多教科书都把函数写在主函数的下面这其实是不对的 ,因为编译在主函数中调用函数时,并没有找到这个函数,到下面才发现这个函数。这时运行时虽然能运行出来,但是编译器会报警告。下面给大家实例。
可以看到编译器提示说Add函数未定义。而此时我们只需要声明一下就可以,我们告诉编译器由这个函数,当函数在下面时就不会报警告。下面我们实践一下
#include <stdio.h>
int Add(int, int);//函数的声明
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 a, int b)
{
return a + b;
}
当我们这串代码声明后可以看到编译器就不会报警告了。
函数的定义
函数的定义是指函数的具体实现,交待函数的功能实现。
一般我们在test.h中放置函数的声明
#ifndef __TEST_H__
#define __TEST_H__
//函数的声明
int Add(int x, int y);
#endif //__TEST_H__
test.c放置函数的实现
函数递归
什么是递归?
程序调用自身的编程技巧称为递归( recursion)。
递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接 调用自身的 一种方法。
它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,
递归策略 只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归存在的两个必要条件
- 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
- 每次递归调用之后越来越接近这个限制条件。
递归的练习(画图理解)
题目是这样的:
接受一个整型值(无符号),按照顺序打印它的每一位。
例如: 输入:1234,输出 1 2 3 4.
//例如: 输入:1234,输出 1 2 3 4.
//%d是打印有符号的整数(会有正负数)
//%u打印无符号的整数(只能是正数)
//这里我们的思路是
//print(1234)
//print(123) 4
//print(12)3 4
//print(1)2 3 4
void print(unsigned int n)
{
if (n > 9)
{
print(n / 10);//打印123
}
printf("%d ", n % 10);//打印4
}
int main()
{
unsigned int n = 0;
scanf("%d", &n);
print(n);//函数将1234的值传过去
return 0;
}
做这个题我们用递归的思路就是大事化小。当我们想输入1234,输出1 2 3 4时我们可以这样去想
我们先打印123的每位输出在屏幕上,在输出4。接下来打印12的每位在打印3在打印4。依此类推。看到这里可能还有很多朋友不理解,我画图为大家讲解。
结语
讲到大家应该有一个大概的感觉,学习编程应该多实践,只听是永远无法理解的,大家可以多通过画图,敲代码去巩固我们所学的知识,特别是递归我在这里讲大家肯定无法理解。需要画图,多思考才能理解。