1.C语言为什么会有函数
-> 整个程序分成多个源文件,一个文件分成多个函数,一个函数分成多个语句,这就是整个程序的组织形式。这样组织的好处在于:分化问题、便于编写程序、便于分工。
-> 函数的出现是人(程序员和架构师)的需要,而不是机器(编译器、CPU)的需要。
-> 函数的目的就是实现模块化编程。说白了就是为了提供程序的可移植性。
2.函数书写的原则:
-> 遵循一定格式。函数的返回类型、函数名、参数列表等。
-> 一个函数只做一件事:函数不能太长也不宜太短,原则是一个函数只做一件事情。
-> 传参不宜过多:在ARM体系下,传参不宜超过4个。如果传参确实需要多则考虑结构体打包。
-> 尽量少碰全局变量:函数最好用传参返回值来和外部交换数据,不要用全局变量。
3.函数是动词、变量是名词
-> 函数将来被编译成可执行代码段,变量(主要指全局变量)经过编译后变成数据或者在运行时变成数据。一个程序的运行需要代码和数据两方向的结合才能完成。
-> 代码和数据需要彼此配合,代码是为了加工数据,数据必须借助代码来起作用。
4.函数的实质是:数据处理器
-> 函数就是程序的一个缩影,函数的参数列表其实就是为了给函数输入原材料数据,函数的返回值和输出型参数就是为了向外部输出目标数据,函数的函数体里的那些代码就是加工算法。
-> 程序的编写和运行就是为了把原数据加工成目标数据,所以程序的实质就是一个数据处理器。
5.函数三要素:定义、声明、调用
-> 函数的定义就是函数体、函数声明是函数原型、函数调用就是使用函数。
-> 函数定义是函数的根本,函数定义中的函数名表示了这个函数在内存中的首地址,所以可以用函数名来调用执行这个函数(实质是指针解引用访问);函数定义中的函数体是函数的执行关键,函数将来执行时主要就是执行函数体。所以一个函数没有定义就是无基之塔。
-> 函数声明的主要作用是告诉编译器函数的原型。
6.函数原型和作用
-> 函数原型的主要作用就是给编译器提供原型,让编译器在编译程序时帮我们进行参数的静态类型检查。
-> 编译器在编译程序时是以单个源文件为单位的(所以一定要在哪里调用在哪里声明),而且编译器工作时已经经过预处理处理了,最最重要的是编译器编译文件时是按照文件中语句的先后顺序执行的。
-> 编译器从源文件的第一行开始编译,遇到函数声明时就会收到编译器的函数声明表中,然后继续向后。当遇到一个函数调用时,就在我的本文件的函数声明表中去查这个函数,看有没有原型相对应的一个函数。
#include <stdio.h>
int add(int a, int b); // 函数声明
int main(void)
{
int a = 3, b = 5;
int sum = add(a, b); // 典型的函数调用
printf("3+5=%d.\n", add(3, 5)); // add函数的返回值作为printf函数的一个参数
return 0;
}
// 函数定义
int add(int a, int b) // 函数名、参数列表、返回值
{
return a + b; //函数体
}
7.递归函数
-> 递归函数就是函数中调用了自己本身这个函数的函数。
-> 递归函数和循环的区别。递归不等于循环。
-> 递归函数解决问题的典型就是:求阶乘、求斐波那契数列。
// 用递归函数来计算阶乘
#include <stdio.h>
int jiecheng(int n); // 函数声明
void digui(int n);
int main(void)
{
digui(300);
//int a = 5;
//printf("%d的阶乘是:%d.\n", a, jiecheng(a));
return 0;
}
// 函数定义
int jiecheng(int n)
{
// 传参错误校验
if (n < 1)
{
printf("n必须大于等于1.\n");
return -1;
}
if (n == 1)
{
return 1;
}
else
{
return (n * jiecheng(n-1));
}
}
void digui(int n)
{
int a[100];
//printf("递归前:n = %d.\n", n);
if (n > 1)
{
digui(n-1);
}
else
{
printf("结束递归,n = %d.\n", n);
}
printf("递归后:n = %d.\n", n);
}
8.函数的递归调用原理:
-> 实际上递归函数是在栈内存上递归执行的,每次递归执行一次就需要耗费一些栈内存。
-> 栈内存的大小是限制递归深度的重要因素。
9.使用递归函数的原则:收敛性、栈溢出
-> 收敛性就是说:递归函数必须有一个终止递归的条件。
-> 递归是占用栈内存的,每次递归调用都会消耗一些栈内存。因此必须在栈内存耗尽之前递归收敛(终止),否则就会栈溢出。
-> 递归函数的使用是有一定风险的,必须把握好。