工欲善其事,必先利其器。
方程是一种数学工具,列方程解方程能够有效解决数学问题。
函数是一种编程技巧,设计函数实现函数能够提高程序设计的效率。
函数之所以难学,是因为它太多变了,有多种形态。
课本不写全吧,作者会被骂。
课堂不讲全吧,老师会被骂。
于是乎,老师满堂灌,学生糊涂蛋。
学生要想不当糊涂蛋,必须拼命地训练。
师傅领进门,修行在个人,绝不是推诿责任,而是事物发展的规律。
客官,如果您有时间,请慢慢听我胡扯。
这样理解:
1、把这个程序整体看做一个学校,a、b、c相当于书记、校长、副校长的名字;
2、main函数相当于学校的一个系;这个程序简单,所以暂时只有一个系;
3、main函数内部相当于系的内部,系内部当然都知道校领导们的名字
怎么样,通俗易懂吧。
注:对于各种程序设计竞赛,主要考察算法,因此总体上程序代码不会太长,而且对效率的要求比较高,采取这种全局变量是经常性的手段。
我们可以像main函数一样,再设计几个其他函数,把程序代码封装起来。
例如:
#include<stdio.h> int a,b,c; void input(void) { scanf("%d%d",&a,&b); } void calculate(void) { c=a+b; } void output(void) { printf("%d\n",c); } void main(void) { input();//输入 calculate();//计算 output();//输出 }(函数调用示意图,来自百度图片搜索)
分析:
1、main函数的代码都不安分,一气化三清,代码独立啦,分解为新的三个函数,input、calculate、output
2、好处是,分而治之好管理了,等大家遇到比较复杂的程序,超过100行那种,就理解得深刻了。
3、如果到此为止,大家一定会说,函数真得太好了,使得程序逻辑更清楚了,也根本没什么难度嘛。
注意:到此为止,你已经能够用函数参加竞赛了,而且如果你算法好的话,拿个冠军都可以。
下面的部分,都是要考虑到大型程序,多人协作时容易产生函数名冲突、变量名冲突,而采取的措施,是越来越复杂。
函数理论复杂就复杂在,他不提倡使用全局变量(即在函数外定义变量),因为多个函数共享变量容易出矛盾,程序结果出现问题不太容易追责。他们的理想是,每一个函数是一个独立王国,只有一个入口和一个出口,来我国的人,必须从入口进,从出口出。也可以把每一个函数比作一个机器,比如榨汁机,苹果塞进去,苹果汁出来;葡萄塞进去,葡萄汁出来。内部怎样实现的,使用的人不必知道。
好了,这样以来,事情变复杂了。就好比两个男女青年谈恋爱,本来是以爱情为主,一见钟情,电光石火,水到渠成。现在非要加上一些其他条件,什么高富帅、白富美,结果剩男剩女一大堆,问题不好优化啊。
我们先改造代码。
分析:
1、为了达到每个函数独立的目的,还需要有理论的支撑。函数外定义的变量叫“全局变量”,函数内定义的变量叫“局部变量”。全局变量、局部变量各在内存的什么地方分配才能高效?
2、mai函数的a、b和input中的a、b只是重名而已,但是他们不在同一个系,不会冲突。什么样的机制才能保障他们不冲突?
3、以上,说出来轻松,编译程序却要精心设计,才能应对变化。
4、最主要的,这些设计的原理,却会成为找工作时面试官的题目。所以在各种课本,函数不仅单独一章,还和其后各章相关联。
因此,同志们,同学们,在大一的阶段,40个课时,只能学基本语法,就像幼儿园学个拼音、儿歌、数个数,哪能讲什么语法,讲了也没什么效果。
C 语言背后原理,或者其他语言背后的原理,有一门课叫做编译原理的,会慢慢到来。
你想,一门课的东西,你非要你的C语言老师给你讲清楚,臣妾估计也是打死都做不到啊。
编译原理三大经典书籍(龙书 虎书 鲸书):
https://blog.csdn.net/shenwansangz/article/details/44217233
编译原理三大圣书——龙书、虎书、鲸…
https://blog.csdn.net/xiaolanmyself/article/details/16944135
同学,你骨骼不太惊奇,不建议你看,知道这三本书的名字,会吹吹牛bi得了。
如果你去看了,我怕你会“学习计算机编程,从入门到放弃”......
函数返回值类型 函数名(参数列表)
{
//函数体
}
上面就是函数的语法形式。
就像是自然语言有语法一样,程序设计语言也是有语法的。
你写文章头疼,编程也会头疼。只不过编程要比写文章简单很多。
其中最难的是,参数列表的写法与解释。
无他,唯手熟尔。
只有多读程序,多写程序,才能够掌握。
可是,亲爱的计算机专业大学生们,每个学期那么多课程,怎么会有时间写程序呢?
无他,唯有舍得,舍弃哪些对你的人生目标不重要的科目,及格就行;把功夫放在编程上,才能得到!
void func(int a,int b)
{.....}
参数 int a,int b;就是最普通的定义方法,因此是传值。
void func(int *a,int *b)
{.....}
参数 int *a,int *b; 是指针的形式,因此a、b中只能存储地址值
int calculate(int a,int b) { int c; c=a+b; return c; }
函数体中可以用 return语句返回一个值。那么return后面那个量的类型是什么,函数的返回值类型就设计成什么。
void func(int a[],int n)
{.......}
和
void func(int *b,int n)
{.......}
这两种写法等价。或者说,前者的本质是后者,前者符合人们日常思维习惯,后者符合计算机计算思维。
#define N 100
void func(int a[][N],int n)
{.......}
和
void func(int *b[100],int n)
{.......}
这两种写法等价。或者说,前者的本质是后者,前者符合人们日常思维习惯,后者符合计算机计算思维。
struct node
{
int a;
int b;
};
void func(struct node mynode){.....}
void func(struct node *mynode){.....}
结构体可以定义复杂类型,其和函数的结合,解释同普通变量
本部分目的:基于函数指针,介绍C语言中快速排序函数的用法。
因此是一石二鸟,既介绍了函数指针,也介绍了快速排序函数。
在此基础上如果你能想到,啊,原理函数指针能够增加函数设计的通用性,有利于代码复用,那你是非常有悟性的。
qsort:quick sort,快速排序。
其出世之日,电闪雷鸣,震惊了世界,其他排序算法的速度不能望其项背。
对于一个整数数组,请分别对其中数据进行升序和降序排列。
•#include <stdlib.h> //头文件
•void qsort( void *buf, size_t num, size_t size, int (*compare)(const void *, const void *) );
•buf:待排序数据的首地址
•num:待排序数据个数
•size:每个数据的大小,单位是字节
•compare:比较函数。
•可以通过重新定义compare比较函数,来调整是升序还是降序。
int (*compare)(const void *, const void *) 这是函数的原型,即你必须按照这种个数来自己定义该函数。
注意:比较函数的名字无所谓,但是其参数和返回值类型是系统规定好的。你必须符合规则。
注意:主调函数在调用qsort时,把比较函数当作参数传递给qsort,其内部机制为:
x会指向a[i],y会指向a[j]
若compare(x,y)返回值为1,则交换a[i]和a[j]的值,否则不交换。
所以你调整比较函数的返回值是 1 还是 -1,就能调整是升序还是降序。
当数据不是简单数据类型时,比如对平面上的点排序,先按照x的大小从小到大排,如果x相等,则按照y的值从小到大排。
#include<stdio.h> #include<stdlib.h> struct point { int x; int y; }; int Asending(const void *x,const void *y) {//升序比较函数 struct point *a,*b; a=(struct point*)x; b=(struct point*)y; if(a->x > b->x) return 1; else if(a->x == b->x) { if(a->y > b->y) return 1; else if(a->y == b->y) return 0; else return -1; } else return -1; } int Desending(const void *x,const void *y) {//升序比较函数 struct point *a,*b; a=(struct point*)x; b=(struct point*)y; if(a->x > b->x) return -1; else if(a->x == b->x) { if(a->y > b->y) return -1; else if(a->y == b->y) return 0; else return 1; } else return 1; } void printArray1D(struct point a[],int n) {//打印数组中数据 int i; for(i=0;i<n;i++) { printf("(%d %d) ",a[i].x,a[i].y); } printf("\n"); } int main() { struct point a[]={{2,8},{2,6},{1,9},{1,7}};//数组 int n=sizeof(a)/sizeof(struct point);//数据个数 printArray1D(a,n) ; //升序排列 qsort(a,n,sizeof(struct point),Asending); printArray1D(a,n) ; //降序排列 qsort(a,n,sizeof(struct point),Desending); printArray1D(a,n) ; return 0; }运行结果:
(over)