1.函数就是一系列语句的组合,包括函数名、返回值、形参表和函数体,例如:主main函数
2.三类:main函数、库函数printf和自定义函数
3.程序的全部工作由函数完成,函数式语言
4.函数不能嵌套定义,但可以互相调用
5.main函数可以调用其它函数,但不能被其它函数调用
6.函数之间通过参数和返回值交换数据
7.int main(void){}为主函数
8.函数也可以没有参数和(或)返回值
C语言函数相关内容(核心中的核心)
1.明确:任何C语言程序都是由两部分组成:一堆的变量(包括数组)和一堆的函数
前者负责分配内存,后者负责操作访问内存
2.函数的概念:就是一堆语句的组合,一次写好,到处使用!printf
本质目的:减少开发的工作量,提高代码的可维护性!
3.问:为何掌握函数这个技术呢?
答:举例子
需求:实现两个正数相加
张三同学的代码:
vim add.c
int main(void) {
int a, b, sum = 0;
scanf("%d%d", &a, &b);
if(a < 0 || b <0) {
printf("请输入两个正数.n");
return -1;
}
sum = a + b;
printf("sum = %dn", sum);
return 0;
}
李四同学代码:
vim add.c
int main(void) {
int a, b, sum = 0;
scanf("%d%d", &a, &b);
if(a < 0 || b <0) {
printf("请输入两个正数.n");
return -1;
}
sum = a + b;
printf("sum = %dn", sum);
return 0;
其余学生:
vim add3.c,....add250.c ...
结论:如果完成这些功能,所有人的代码很多地方都是重复的,写了一遍又一遍,无形增加开发的工作量和代码体积
期望:只需将重复的代码写一次即可,其他人无需编写拿来直接使用即可,类似:大神写好的printf,scanf一样
极大减少开发的工作量
问:如何实现?
答:采用大名鼎鼎的函数这个技术来解决
大佬编写一个add函数
vim add.c
void add(int x, int y) {
if(x < 0 || y <0) {
printf("请输入两个正数.n");
return;
}
sum = x + y;
printf("sum = %dn", sum);
}
张三同学的代码:
vim add.c
int main(void) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);//调用大佬编写的add函数并且给add函数传递两个数值也就是a,b的值
return 0;
}
李四同学代码:
int main(void) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);//调用大佬编写的add函数并且给add函数传递两个数值也就是a,b的值
return 0;
}
结论:如果将来要想实现这个需求,只需调用大佬编写的add函数即可,无需非常辛苦的编写代码
大大提高了开发的效率,提高代码的可维护性,大大降低了开发的工作量
函数三大步骤 函数定义:(函数实现,函数封装)
返回值类型 函数名(形参表){
函数体;
}
1.函数的返回值类型默认为int,若没有返回值,则需要用void标明
2.若函数的返回值类型为void,则不需要return任何值,或者省略return语句,若加了return,后边也不要加任何值
3.若函数的返回值类型不是void,但没有通过return语句显式返回任何值,则会返回一个不确定值
4.若函数的返回值类型与return值的类型不一致,则会发生类型转换
遇到return时,编译器相当于加一条goto语句,跳到末尾的花括号
函数调用:1.函数在定义时使用的参数叫形参,调用函数时传入的参数叫实参
2.函数调用时将实参传递给形参的过程, 类似于把实参赋值给形参
3.形参只是实参的一份拷贝,而非实参本身,这种参数传递方式叫值传递
示例:
//函数的用法 #include//函数玩法步骤一:函数定义 //形式1:定义无返回值无形参的函数 //无返回值:就是函数执行完毕不需要返回一个数值 //无形参:就是函数不需要接收传递来的参数 void foo(void) { printf("void foo(void)n"); //return; //可以省略return关键字 } void foo1(void) { printf("void foo1(void)n"); return;//如果是无返回值函数,并且用return进行返回,此时return后面不要跟数值 printf("我会被执行吗?n"); } //形式2:定义有返回值无形参的函数 //有返回值:就是函数执行完毕最后返回一个运行的结果 int bar(void) { //int:表示bar函数返回运行结果的数据类型 return 520; //返回常量 } int bar2(void) { int a = 521; a++; return a; //返回变量a的内存值522 } int bar3(void) { printf("int bar3(void)n"); } //忘记写return,忘记添加返回值,gcc会返回一个随机数 char bar4(void) { int a = 555; return a;//gcc会将int类型的a强转为char类型之后在返回 } //形式3:定义无返回值有形参的函数 //有形参:就是调用函数时可以给函数传递参数,函数的形参可以用来保存传递的参数 //形参就是一堆变量,此变量又称形参变量 void add(int x, int y) { //x,y就是形参变量,分配的内存用来保存调用函数时给函数传递的参数值 //形参变量x保存实参变量a的值10,形参变量y保存实参变量b的值20 if(x < 0 || y < 0) { printf("传递的参数值不是正数.n"); return; } int sum = x + y; printf("add函数:sum = %dn", sum); } //形式4:定义有返回值并且有形参的函数 int sub(int x, int y) { return x - y; //返回形参变量x,y相减的结果 } int main(void) { printf("main函数开始.n"); //函数玩法步骤二:函数调用 int a = 10, b = 20; printf("sub函数的返回值是:%dn", sub(a, b)); add(a, b);//调用无返回值有形参的函数,并且调用时给add函数传递两个实参变量a,b的值10,20 int ret = 0; //ret变量将来保存函数的返回值 ret = bar(); //调用有返回值无形参函数,用ret来保存函数的返回值 printf("bar函数的返回值是:%dn", ret); ret = bar2(); //调用有返回值无形参函数,用ret来保存函数的返回值 printf("bar2函数的返回值是:%dn", ret); ret = bar3(); //调用有返回值无形参函数,用ret来保存函数的返回值 printf("bar3函数的返回值是:%dn", ret); ret = bar4(); //调用有返回值无形参函数,用ret来保存函数的返回值 printf("bar4函数的返回值是:%dn", ret); printf("bar4函数的返回值是:%dn", bar4()); foo();//调用无返回值无形参的函数 foo1();//调用无返回值无形参的函数 printf("main函数结束.n"); return 0; }
函数声明:不会分配内存
1.函数在使用前必须声明,声明可以是隐式的,隐式声明的固定形式为:
int f(); //该函数可以接受任意参数,并返回整型值
但是!!!!虽然可以接受任意参数,函数却不能对这些传递的参数做任何操作,因为没有形参去接收
2.如果函数的返回类型不是int,最好对其做显式声明,即函数的原型
extern 返回值类型 函数名(形参表)
3.声明函数时,省略参数表,表示其可接受任意参数;参数表为void,表示其不接受任何参数
4.下面代码调用上面定义的函数时,可以不再单独声明
示例:
//函数的用法 #include//函数步骤三:函数声明(不会分配内存的) //切记:如果函数调用的代码在函数定义的代码前面,那么就需要在函数调用代码的前面先做函数声明 extern void foo(void); extern void foo1(void); extern int bar(void); extern int bar2(void); extern int bar3(void); extern char bar4(void); extern void add(int x, int y); extern int sub(int, int); //声明时形参变量名可以省略 int main(void) { printf("main函数开始.n"); //函数步骤二:函数调用 int a = 10, b = 20; printf("sub函数的返回值是:%dn", sub(a, b)); add(a, b);//调用无返回值有形参的函数,并且调用时给add函数传递两个实参变量a,b的值10,20 int ret = 0; //ret变量将来保存函数的返回值 ret = bar(); //调用有返回值无形参函数,用ret来保存函数的返回值 printf("bar函数的返回值是:%dn", ret); ret = bar2(); //调用有返回值无形参函数,用ret来保存函数的返回值 printf("bar2函数的返回值是:%dn", ret); ret = bar3(); //调用有返回值无形参函数,用ret来保存函数的返回值 printf("bar3函数的返回值是:%dn", ret); ret = bar4(); //调用有返回值无形参函数,用ret来保存函数的返回值 printf("bar4函数的返回值是:%dn", ret); printf("bar4函数的返回值是:%dn", bar4()); foo();//调用无返回值无形参的函数 foo1();//调用无返回值无形参的函数 printf("main函数结束.n"); return 0; } //函数步骤一:函数定义 //形式1:定义无返回值无形参的函数 //无返回值:就是函数执行完毕不需要返回一个数值 //无形参:就是函数不需要接收传递来的参数 void foo(void) { printf("void foo(void)n"); //return; //可以省略return关键字 } void foo1(void) { printf("void foo1(void)n"); return;//如果是无返回值函数,并且用return进行返回,此时return后面不要跟数值 printf("我会被执行吗?n"); } //形式2:定义有返回值无形参的函数 //有返回值:就是函数执行完毕最后返回一个运行的结果 int bar(void) { //int:表示bar函数返回运行结果的数据类型 return 520; //返回常量 } int bar2(void) { int a = 521; a++; return a; //返回变量a的内存值522 } int bar3(void) { printf("int bar3(void)n"); } //忘记写return,忘记添加返回值,gcc会返回一个随机数 char bar4(void) { int a = 555; return a;//gcc会将int类型的a强转为char类型之后在返回 } //形式3:定义无返回值有形参的函数 //有形参:就是调用函数时可以给函数传递参数,函数的形参可以用来保存传递的参数 //形参就是一堆变量,此变量又称形参变量 void add(int x, int y) { //x,y就是形参变量,分配的内存用来保存调用函数时给函数传递的参数值 //形参变量x保存实参变量a的值10,形参变量y保存实参变量b的值20 if(x < 0 || y < 0) { printf("传递的参数值不是正数.n"); return; } int sum = x + y; printf("add函数:sum = %dn", sum); } //形式4:定义有返回值并且有形参的函数 int sub(int x, int y) { return x - y; //返回形参变量x,y相减的结果 }
数组访问公式!!
//函数访问数组分配的内存编程公式 #include//函数玩法三:函数声明 extern void print(int parray[], int size); extern void change(int parray[], int size); int main(void) { int array[5] = {1,2,3,4,5}; int size = sizeof(array)/sizeof(array[0]); //函数玩法二:函数调用 print(array, size);//调用print函数来打印数组的元素值,并且将数组的首地址和元素个数传递给 //print函数 change(array, size);//传递数组的首地址和元素个数 print(array, size);//传递数组的首地址和元素个数 return 0; } //函数玩法一:函数定义 //第一个参数parray不是数组,本质是指针(后续课程详解),parray保存着数组的首地址 //必须是此形式,[]千万不能丢弃,函数print将来通过parray来访问数组跟通过数组名array访问数组 //一模一样 void print(int parray[], int size) { for(int i = 0; i < size; i++) printf("%d ", parray[i]); printf("n"); } void change(int parr[], int size) { for(int i = 0; i < size; i++) parr[i] += 1; }
extern void print(int parray[], int size); //这里定义的形参用 int parry[]来接收,不能去掉[],这是公式!!!!!
print(array, size);//调用print函数来打印数组的元素值,并且将数组的首地址和元素个数传递给print函数
change(array, size);//传递数组的首地址和元素个数
print(array, size);//传递数组的首地址和元素个数
第一个参数parray不是数组,本质是指针(后续详解),parray保存着数组的首地址
必须是此形式,[]千万不能丢弃,函数print将来通过parray来访问数组跟通过数组名array访问数组
递归与递推:
1.函数在函数体内调用其自身称为调用该函数称为递归函数
2.调用递归函数的过程,逐层调用,逐层返回
3.递归就是循环迭代,上一代运算结果作为下一次的运算初始值,版本更新就是迭代过程
4.递归有可能形成无限递归,或者增加算法的时间复杂度,因此使用递归时,需要注意:
必须有递归终止条件
必须保证应用递归确实使算法得到简化
段错误(核心已转储):内存非法访问了,崩溃
递归函数调用流程:
A<->B<->C
A<->A<->A
需求:利用递归函数实现打印:1 2 3
分析:
#include
void print(int max) {
//3.最后添加递归退出的条件
if(max == 1) {
printf("1 ");
return;
}
print(max-1);//2.然后发现打印2和打印3所作的工作都是一样的,只需调用自己重复打印即可
printf("%d ", max);//1.首先要明确此递归函数最终要完成一个打印数字的功能
return;
}
int main(void) {
int max = 3;
print(max);
printf("n");
return 0;
}



