一、函数概念
编程中的函数和数学中的函数意思不太一样,但本质都是一样,都是为了实现某种功能,作用。函数的英语叫做 function,意思就是功能。在计算机中我们将某段代码实现某种功能定义成一个函数(代码块,代码段),这样的作用在写很多代码的时候,给人一种清晰,逻辑性强,能看得懂的感觉,可以减少编程后期的维护。比如说:和平精英某个技能失效了,我们不能去和平精英所有的代码中去寻找没有起作用的代码来查找问题,我们只需要到实现这个功能的地方去查找就可以了。这样我们在维护的时候就减少了大量的工作,效率提升了,骂人少了,社会更加和谐了。
用于完成特定任务的程序代码单元
那就是把一个小功能封装成一个独立的代码段(函数)
封装前与封装后的执行结果是一样的。封装的意思可以理解为打包起来。类似就是一个工具,我们一开始就设置好这个工具的使用方法,需要的时候直接使用这个工具即可,这样就不需要在每次使用这个工具的时候还要再重新定义这个工具的使用方法了。面向过程中的函数封装和面向对象中的类封装或者方法封装本质上是一个意思。
二、函数的作用
2.1 增加代码的复用性(重复使用)
// 数组查找最大值的例子 // 不使用函数 #include<stdio.h> int main(void) { int a[5] = {12, 3, 45, 6, 7}; int b[6] = { 23, 45, 12, 76, 4, 6 }; int maxNuma = a[0]; int maxNumb = b[0]; int i; for (i = 1; i < 5; i++) { if (maxNuma < a[i]) { maxNuma = a[i]; } } for (i = 1; i < 6; i++) { if (maxNumb < b[i]) { maxNumb = b[i]; } } printf("maxNuma = %d\n", maxNuma); printf("maxNumb = %d\n", maxNumb); return 0; } // 上面的代码是找出数组中的最大的值。我们查找了一个数组需要将查找的算法写一次,查找另一个数组还需要再写一次,
这样同样的事情就多做了一次。如果需要查找的数组太多,重复写同样的代码让人感觉自己被时代抛弃。除了能增加代码量
的好处外,暂时想不到其他的好处了,可能以后会想到。// 使用函数 #include<stdio.h> int fun(int* p, int length) { int i; int maxNum = p[0]; for (i = 1; i < length; i++) { if (maxNum < a[i]) { maxNum = a[i]; } } return maxNum; } int main(void) { int a[5] = {12, 3, 45, 6, 7}; int b[6] = { 23, 45, 12, 76, 4, 6 }; int maxNuma = a[0]; int maxNumb = b[0]; maxNuma = fun(a, 5); maxNumb = fun(b, 6); printf("maxNuma = %d\n", maxNuma); printf("maxNumb = %d\n", maxNumb); } /* 从上面中可以看出来,使用了函数,代码看起来更加简洁,具有条理性,效率提高,
让人感觉社会在进步。不管给出什么样的数组,我只需要调用一次封装的函数即可,任尔东西南北风,我自岿然不动。*/
// 数组的增删改查及本操作 #include <stdio.h> #include <malloc.h> // 动态分配内存 #include <stdlib.h> // exit退出 #include <stdbool.h> // bool类型 struct Arr { int *pBase; int len; int cnt; }; void InitArray(struct Arr *pArr, int length); bool ShowArray(struct Arr *pArr); bool AppendEle2Array(struct Arr *pArr, int val); bool InsertEle2Array(struct Arr *pArr, int pos, int val); bool DeleteArrayEle(struct Arr *pArr, int pos, int *pVal); bool ArrayFull(struct Arr *pArr); void InversionArray(struct Arr *pArr); void SortArray(struct Arr *pArr); int main() { struct Arr arr; int val; // 初始化数组 InitArray(&arr, 8); // 给数组添加元素 AppendEle2Array(&arr, 1); AppendEle2Array(&arr, 2); AppendEle2Array(&arr, 3); AppendEle2Array(&arr, 4); // 插入元素到数组中 InsertEle2Array(&arr, 3, 99); AppendEle2Array(&arr, 5); AppendEle2Array(&arr, 6); // 删除数组元素 DeleteArrayEle(&arr, 4, &val); AppendEle2Array(&arr, 7); // 翻转数组 InversionArray(&arr); // 数组排序 SortArray(&arr); // 展示数组 ShowArray(&arr); return 0; } // 初始化数组 void InitArray(struct Arr *pArr, int length) { pArr->pBase = (int *) malloc(sizeof(int) * length); if (pArr->pBase == NULL) { printf("动态内存分配失败!\n"); exit(-1); } else { pArr->len = length; pArr->cnt = 0; } } // 给数组添加元素 bool AppendEle2Array(struct Arr *pArr, int val) { if (ArrayFull(pArr)) { return false; } else { pArr->pBase[pArr->cnt] = val; (pArr->cnt)++; return true; } } // 判断数组是否满了 bool ArrayFull(struct Arr *pArr) { if (pArr->cnt == pArr->len) { return true; } else { return false; } } // 判断数组是否为空 bool ArrayEmpty(struct Arr *pArr) { if (pArr->cnt == 0) { return true; } else { return false; } } // 展示数组 bool ShowArray(struct Arr *pArr) { if (ArrayEmpty(pArr)) { return false; } else { for (int i = 0; i < pArr->cnt; i++) { printf("%d,", pArr->pBase[i]); } } return true; } // 插入元素到数组中 bool InsertEle2Array(struct Arr *pArr, int pos, int val) { if (ArrayFull(pArr)) { return false; } if (pos < 1 || pos > pArr->cnt + 1) { return false; } for (int i = pArr->cnt - 1; i >= pos - 1; --i) { pArr->pBase[i + 1] = pArr->pBase[i]; } pArr->pBase[pos - 1] = val; (pArr->cnt)++; return true; } // 删除数组元素 bool DeleteArrayEle(struct Arr *pArr, int pos, int *pVal) { if (ArrayEmpty(pArr)) { return false; } if (pos < 1 || pos > pArr->cnt + 1) { return false; } *pVal = pArr->pBase[pos - 1]; for (int i = pos; i < pArr->cnt; i++) { pArr->pBase[i - 1] = pArr->pBase[i]; } (pArr->cnt)--; return true; } // 翻转数组 void InversionArray(struct Arr *pArr) { int i = 0; int j = pArr->cnt - 1; int t; while (i < j) { t = pArr->pBase[j]; pArr->pBase[j] = pArr->pBase[i]; pArr->pBase[i] = t; i++; j--; } } // 数组排序 void SortArray(struct Arr *pArr) { int i, j, k; for (i = 0; i < pArr->cnt; i++) { for (j = i + 1; j < pArr->cnt; j++) { if (pArr->pBase[i] > pArr->pBase[j]) { k = pArr->pBase[i]; pArr->pBase[i] = pArr->pBase[j]; pArr->pBase[j] = k; } } } } /* 主逻辑清晰,便于确定问题所在,方便修改维护.我们能够感觉到代码逻辑清晰,需要什么功能直接去相应的地方查找即可。让人感觉世界充满爱。 注意: 通常,会把一个功能封装成一个函数 一个函数尽量是单一的功能,干啥就是专门干啥的 函数的封装无关于代码量 */
面向过程和面向对象编程本质上的逻辑顺序都是从上往下的顺序结构。在编译型语言(c, java)当中,代码一般都是从 main函数中开始运行。当遇到调用函数的位置,会直接跳转进函数,函数走完了,再跳回调用位置。在解释型语言中,一般都是直接运行代码,代码从上往下一步一步执行。当遇到函数调用的时候,跳转到调用的位置,函数执行结束,跳回原本的位置继续执行。
函数比直接写的效率是低那么一点点的,因为涉及到跳转一步操作,但是一步操作,实在太微乎其微 了,就像是地球上的一只蚂蚁。因为微乎其微,我们就可以适当的忽略不计。除非在特定场景中,比如一些占用时间的IO操作,多进程多线程中可能会考虑到效率问题再解决,但是大部分情况下函数是实际编程中必用的,也完全不用考虑函数的个数带来的稍慢。