函数在我们C语言里可谓是经常可以用到,所以了解函数,学会函数怎么使用是很重要的。
目录
4.2嵌套调用
1.什么是函数?
- 函数是一个大型程序中的某部分代码,由一个或多个语句块组成,它负责完成某个特定的任务,相较于其它代码,函数具有相对独立性。
- 一般有输入参数和返回值,提供对过程的分装和细节的隐藏。
2.函数分类
函数分为库函数和自定义函数。
2.1库函数
库函数分类
- I/O函数 :input与output函数 printf scanf getchar putchar等
- 字符串操作函数:strlen strcmp等
- 字符操作函数:大小写转换 字符分类等
- 内存操作函数:memcpy memove memset等
- 时间/日期函数:time等
- 数学函数:pow sqrt等
- 其它库函数
使用库函数
这里给大家推荐两个网站来了解C语言中的库函数,在网站里我们可以查找对应库函数如何使用,它所对应的头文件。
www.cplusplus.com(英文版)
http://zn.cppreference.com(中文版)
在这里给大家演示一个库函数
strcpy函数——string copy 字符串拷贝
在红框框柱的地方我们输入要查找的库函数,就会找到它所对应的内容
这样我们就了解了一个库函数的基本使用方法,让我们用这个库函数实现把一个字符串复制到另一个字符串中。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "hello world!!!" ;
char arr2[] = "##################################";
printf("%s\n", arr2);
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
我们可以看到字符串arr1被复制到了arr2,实现了库函数的使用。
2.2自定义函数
库函数在很大程度上确实方便了编程,但是作为一个程序员,最重要的还是要学会自定义函数,我们总会遇到库函数里所没有模板,函数自定义能力才是最有力的工具。
自定义函数的函数名、返回类型、函数参数都是由我们自己来设计的。
return_type funcation_name(paral *)
{
starement;
}
下面我们来看个例子:例如用函数实现求俩个数中的较大值。
#include<stdio.h>
int Max(int num1, int num2)
{
return(num1 > num2 ? num1 : num2);
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int max = Max(a, b);
printf("max=%d\n", max);
return 0;
}
我们可以清楚的看到这个函数的函数名、返回类型、函数参数都是由我们自己来设计的,max接受函数Max的返回值。
我们再来看一个例子,用函数的方法交换俩个整型变量的内容。
#include<stdio.h>
void swap1(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", a, b);
printf("a=%d,b=%d\n", a, b);
swap1(a, b);
printf("a=%d,b=%d\n", a, b);
return 0;
}
我们依旧用定义上一个函数的方法定义这个函数,我们会发现函数没有使两个变量交换内容,没有达到交换俩个整型值的目的。因为这涉及到传值调用还是传址调用的问题,我们把这个问题放到函数调用的目录下解决。
3.函数的参数
3.1实际参数
函数的实际参数是指真实传递给函数的参数。
实参可以是:常量,变量,表达式,函数等。
无论实参是何种类型的量,在进行函数调用时,它们都必须是确定的值,以便把这些值传递给形参。
3.2形式参数
函数的形式参数指的是函数名后括号中的变量。
形参只有在函数被调用过程中才实例化(即给函数分配内存单元),形参在函数调用完成后就自动销毁了,它只在函数中起作用。
当实参传给形参时,形参其实是实参的一份临时拷贝,对形参的修改是不会改变实参的。
4.函数的调用
4.1分类
- 传值调用:函数的形参和实参分别占用不同的内存块(即a,b,x,y它们的地址是不同的),对形参的修改是不会影响实参的。所以回到我们交换两个整型变量内容的问题上
可见,我们在这里用的是传值调用,当实参传给形参时,形参其实是实参的一份临时拷贝,对形参的修改是不会改变实参的。所以x和y的值虽然在函数体中交换了,但它们交换后的值是无法传递给a,b的,即a,b的值依然没有交换,所以我们引入传址调用。
- 传址调用:把函数外部创建的内存地址传递给函数参数的方式。这种方式可以让函数和函数外边的变量真正建立起联系,也就是函数内部可以直接操作函数外部的变量。
这里我们在来解决这个问题:
#include<stdio.h>
void swap2(int* x, int* y)
{
int tmp = 0;
tmp = *x;
*x = *y;
*y = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d",& a, &b);
printf("a=%d,b=%d\n", a, b);
swap2(&a, &b);
//swap1(a, b);
printf("a=%d,b=%d\n", a, b);
return 0;
}
这种直接传地址的方法,交换了整型变量的内容。
其实想要区分清楚什么时候用传值调用,什么时候用传址调用很简单。在这里给大家分享我的办法。
不改变实参内容的用传值调用,要改变实参的值的用传址调用。
函数有返回值的一般是传值调用(因为笔者目前还没有见过有返回值的是传址调用),函数无返回值的大多是传址调用(除非直接在函数内部打印)。
4.2嵌套调用
看个例子来理解它
4.3链式访问
链式访问就是把一个函数的返回值作为另一个函数的参数。
5.函数的声明
创建一个头文件,文件名后缀为.h,将函数的声明放入头文件,例如
#ifndef __ADD_H__
#define __ADD_H__
int binary_search(int arr[], int k, int sz);
#endif
再创建一个源文件add.c,这里放函数的定义,即函数是怎么实现的。
int binary_search(int arr[], int k, int sz)
{
int left = 0;
int right = sz - 1;
int mid = 0;
while (left <= right)
{
mid = (left + right) / 2;
if (arr[mid] < k)
left = mid++;
else if (arr[mid] > k)
right = mid--;
else
return mid;
}
return -1;
}
最后在我们原来创建的源文件中留下我们的主函数
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int k = 7;
int sz = sizeof(arr) / sizeof(arr[0]);
int ret = binary_search(arr, k, sz);
if (ret == -1)
printf("Ҳ\n");
else
printf("ҵˣ±%d\n", ret);
return 0;
}
1.函数声明是告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但具体存不存在,无关紧要。
2.函数声明一般出现在函数使用之前,要满足先声明在使用。
3.函数的声明一般放在头文件中。
6.函数的递归与迭代
6.1递归
函数调用自身编程技巧为递归,即一个函数自己调用自己,直接调用或间接调用自身的一种方法。
递归的主要思想是把大事化小。
接收一个整型值(无符号),按顺序打印它的每一位。例如输入1234,输出1 2 3 4 。
void print(int n)
{
if (n > 9)
{
print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
unsigned int num = 0;
scanf("%d", &num);
print(num);
return 0;
}
递归的两个必要条件:
- 存在限制条件,当满足这个条件时,递归便不再继续。
- 每次递归调用之后越来越接近这个限制条件。
6.2迭代
迭代就是一直重复做一件事情。
例如求第n个斐波那契数
#include<stdio.h>
int Fib(int num)
{
int a = 1;
int b = 1;
int c = 1;
while (num > 2)
{
c = a + b;
a = b;
b = c;
num--;
}
return c;
}
int main()
{
int n = 0;
int ret = 0;
scanf("%d", &n);
ret= Fib(n);
printf("ret=%d\n", ret);
return 0;
}
这种迭代的方法在这个算法中比递归更加高效。
递归和迭代的选择
当俩种方法在使用时都没有问题时,可随意选择;
当在使用时存在明显问题如栈溢出,这种方法就不可以使用了。
OK了,各位家人们,今天的分享就到这里啦,有什么问题欢迎评论。