提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
在计算机科学中,子程序,是一个大型程序中的某部分代码,有一个或多个语句块组成。
它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。
这些代码通常被集成为软件库。
一、库函数
C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发
查询工具:
网址www.cplusplus.com
网址cppreference.com-国内镜像 (gitee.io)
网址cppreference.com
二、自定义函数
自定义函数和库函数一样,有函数名,返回值类型和函数参数。
但不一样的是这些都是我们自己来设计。这给程序员一个很大的操作空间,例如我们自己设计一个加法函数.
int Add(int x,int y)
{
return (x+y);
}
写一个函数选出最大值
int get_max(int x,int y) int因为返回的是整型,所以要用整型接收
{
if(x > y)
return x;
else
return y;
}
int main()
{
int a;
int b;
scanf("%d%d",&a,&b);
int max = get_max(a,b);
printf("max = %d",max);
return 0;
}
写一个函数可以交换两个整型变量的内容
void表示无,空,没有返回值,swap只是交换,不需要返回值
void swap(int a,int b)
{
int temp;
temp = b;
b = a;
a = temp;
}
int main()
{
int a = 30;
int b = 20;
swap(a,b)
printf("a = %d b = %d\n",a,b);
return 0;
}
实际运行之后会发现,a和b的值并没有交换,再往下看
三、函数的参数
实际参数(实参)
实际参数是指真实传给函数的参数:swap(a,b)传递给swap函数的参数a,b
实参可以是:常量、变量、表达式、函数等
无论实参是何种类型的量,在进行函数调用时,他们都必须有确定的值,以便把这些值传送个形参
当实参传给形参的时候,形参其实是实参的一份临时拷贝,对形参的修改是不会改变实参的!!!
形参和实参的名字相同也没有影响
形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元即申请空间地址)
形式参数当函数调用完成后就自动销毁
形式参数只在函数中有效
对形参的修改不会影响实参!!!
举例
前面说到这个程序运行时a与b的值无法交换
形参和实参的名字相同也没有影响
void swap(int a,int b) 此时的a和b是形参
{
int temp;
temp = b;
b = a;
a = temp;
}
这个swap函数里面的a和b是形参,是main函数里a和b的一份临时拷贝
main函数里的a和b有一块自己的内存空间,当调用swap交换函数时会分配内存单元即申请新的内存空间(a和b的内存空间)
这块新的空间与原来main函数的空间不同,交换的内容是新的空间而不是main函数里a和b的空间
所以交换不会成功,a和b的值没有改变
因此形式参数只在函数中有用
int main()
{
int a = 30;
int b = 20;
swap(a,b) 此时的a和b是实参
printf("a = %d b = %d\n",a,b);
return 0;
四、函数调用
函数的形参和实参分别占有不同的内存块,对形参的修改不会影响到实参
传值调用
函数的形参和实参分别占有不同的内存块,对形参的修改不会影响实参。
swap(a,b)就是传值调用
传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作的函数外部的变量
swap(&a,&b)就是传址调用
五、函数的嵌套调用和链式访问
函数和函数之间是可以有机组合的
嵌套调用
在函数中调用其他函数
#include<stdio.h>
void new_line()
{
printf("hehe\n");
}
void three_line()
{
int i = 0;
for(i = 0;i < 3;i++)
{
new_line();
}
}
int main()
{
three_line();
return 0;
}
链式访问
把一个函数的返回值作为另一个函数的参数
#include<stdio.h>
int main()
{
int len = 0;
len = strlen("abc");
printf("%d\n",len);
printf("%d\n,stelen("abc")); 链式访问
return 0;
}
#include<stdio.h>
int main()
{
printf("d",printf("%d",printf("%d",43)));
return 0;
}
打印结果为4321 printf的使用规则
printf("d",printf("%d",printf("%d",43))); 先打印43后,43只有两个数字
printf("d",printf("%d",2)); 只有两个数字,就变成2
printf("d",1); 2是一个数字,就变成1
六、函数的声明和定义
声明和定义
先来看这样的一段代码
int main()
{
int a = 10;
int b = 20;
int sum = 0;
sum = Add(a,b);
printf("%d\n",sum);
return 0;
}
//函数的定义
int Add(int x,int y)
{
int z = x + y;
return z;
}
运行之后会报错,原因是代码是从上往下读的,编译器还没有看到Add函数就是用,编译器就懵逼了,所以要先进行声明
//函数声明
int Add(int x,int y);
int main()
{
int a = 10;
int b = 20;
int sum = 0;
sum = Add(a,b);
printf("%d\n",sum);
return 0;
}
//函数的定义
int Add(int x,int y)
{
int z = x + y;
return z;
}
声明之后,编译器知道了有Add函数这个东西,就能运行下去
应用场景分文件处理
```c
头文件(add.h):在头文件中声明函数
//函数的声明
int Add(int x,int y);
源文件(add.c):在源文件中创造函数
int Add(int x,int y)
{
return x + y;
}
test.c(要使用的)
#include "add.h"
使用说明:引用头文件#inlcude "add.h"即可在不同的源文件中使用
七、函数递归
内存存储分布
调用函数会在栈区申请内存空间,不断持续的调用会导致栈溢出
递归是什么?
程序调用自身的编程技巧成为递归。
递归作为一种算法在程序设计语言中广泛应用。
一个过程或函数在其定义或说明中有直接或简介调用自身的一种方法,
通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
递归策略只需要少量的程序就可以描述出解题过程中所需要的多次重复计算,大大地吉拿少了程序的代码量
递归的主要思考方式在于:把大事化小
int main()
{
printf("hehe");
main();
return 0;
}
这就是一个简单的递归函数,不停的调用函数
最后会栈溢出
每调用一次main函数,都会向栈区申请空间,当空间被塞满就会栈溢出。
调用函数主要向栈区申请内存空间
递归的两个必要条件
1.存在条件限制,当满足这个条件限制的时候,递归便不在继续。
2.每次递归调用之后越来越接近这个限制条件。
练习
1.用递归求n的阶乘(不考虑溢出)
int Fac(int n)
{
if (n < 1)
return 1;
1.存在条件限制,当满足这个条件限制的时候,递归便不在继续。
else
return n * Fac(n - 1);
2.每次递归调用之后越来越接近这个限制条件。
}
int main()
{
int n;
scanf("%d", &n);
int ret = Fac(n);
printf("%d\n", ret);
return 0;
}
2.描述第n个斐波那契数列
int Fib(int n)
{
if (n <= 2)
return 1;
1.存在条件限制,当满足这个条件限制的时候,递归便不在继续。
else
return Fib(n - 1) + Fib(n - 2);
2.每次递归调用之后越来越接近这个限制条件。
}
int main()
{
int n;
scanf("%d", &n);
int ret = Fib(n);
printf("%d\n", ret);
return 0;
}
3.递归实现将参数字符串中的字符反向排列
#include<stdio.h>
int my_strlen(char arr[])
{
int count = 0;
int i = 0;
while (arr[i] != '\0')
{
count++;
i++;
}
return count;
}
void revers_string(char arr[])
{
int tmp = arr[0];
int len = my_strlen(arr);
arr[0] = arr[len - 1];
arr[len - 1] = '\0';
if (my_strlen(arr + 1) >= 2)
revers_string(arr + 1);
arr[len - 1] = tmp;
}
int main()
{
char arr[] = "abcdefg";
revers_string(arr);
printf("%s", arr);
return 0;
}
4.计算一个数的每位之和
int Digitsum(int n)
{
if (n > 9)
{
return n % 10 + Digitsum(n / 10);
}
else
return n;
}
int main()
{
int a = 0;
scanf("%d", &a);
int sum = Digitsum(a);
printf("%d", sum);
return 0;
}
5.递归实现k的n次方
double Pow(int n, int k)
{
if (k < 0)
return (1.0 / (Pow(n, -k)));
else if (k == 0)
return 1;
else
return n * Pow(n, k - 1);
}
int main()
{
int n = 0;
int k = 0;
scanf("%d%d", &n,&k);
double ret = Pow(n, k);
printf("%lf", ret);
return 0;
}