C语言-函数-学会方程你的数学能力会乘风破浪突飞猛进-学会函数你的编程能力将百尺竿头更进一步

工欲善其事,必先利其器。

方程是一种数学工具,列方程解方程能够有效解决数学问题。

函数是一种编程技巧,设计函数实现函数能够提高程序设计的效率。

函数之所以难学,是因为它太多变了,有多种形态。

课本不写全吧,作者会被骂。

课堂不讲全吧,老师会被骂。

于是乎,老师满堂灌,学生糊涂蛋。

学生要想不当糊涂蛋,必须拼命地训练。

师傅领进门,修行在个人,绝不是推诿责任,而是事物发展的规律。

客官,如果您有时间,请慢慢听我胡扯。

一、建议初学者,把变量定义全部写在函数外,成为全局变量

#include<stdio.h>
int a,b,c;
void main(void)
{
	scanf("%d%d",&a,&b);
	c=a+b;
	printf("%d\n",c);	
}

这样理解:

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、如果到此为止,大家一定会说,函数真得太好了,使得程序逻辑更清楚了,也根本没什么难度嘛。

注意:到此为止,你已经能够用函数参加竞赛了,而且如果你算法好的话,拿个冠军都可以。

下面的部分,都是要考虑到大型程序,多人协作时容易产生函数名冲突、变量名冲突,而采取的措施,是越来越复杂。


函数理论复杂就复杂在,他不提倡使用全局变量(即在函数外定义变量),因为多个函数共享变量容易出矛盾,程序结果出现问题不太容易追责。他们的理想是,每一个函数是一个独立王国,只有一个入口和一个出口,来我国的人,必须从入口进,从出口出。也可以把每一个函数比作一个机器,比如榨汁机,苹果塞进去,苹果汁出来;葡萄塞进去,葡萄汁出来。内部怎样实现的,使用的人不必知道。

好了,这样以来,事情变复杂了。就好比两个男女青年谈恋爱,本来是以爱情为主,一见钟情,电光石火,水到渠成。现在非要加上一些其他条件,什么高富帅、白富美,结果剩男剩女一大堆,问题不好优化啊。

我们先改造代码。

三、改造我们的代码

#include<stdio.h>

void input(int *a,int *b)
{
	scanf("%d%d",a,b);
}
int calculate(int a,int b)
{
	int c;
	c=a+b;
	return c;
}
void output(int c)
{
	printf("%d\n",c);
}
void main(void)
{
	int a,b,c;
	input(&a,&b);//输入 
	c=calculate(a,b);//计算 
	output(c);//输出
		
}

分析:

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得了。

如果你去看了,我怕你会“学习计算机编程,从入门到放弃”......

四、函数语法

函数返回值类型   函数名(参数列表)

{

//函数体

}

上面就是函数的语法形式。

就像是自然语言有语法一样,程序设计语言也是有语法的。

你写文章头疼,编程也会头疼。只不过编程要比写文章简单很多。

其中最难的是,参数列表的写法与解释。

无他,唯手熟尔。

只有多读程序,多写程序,才能够掌握。

可是,亲爱的计算机专业大学生们,每个学期那么多课程,怎么会有时间写程序呢?

无他,唯有舍得,舍弃哪些对你的人生目标不重要的科目,及格就行;把功夫放在编程上,才能得到!

1、简单数据传值

void func(int a,int b)

{.....}

参数 int a,int b;就是最普通的定义方法,因此是传值。

2、简单数据传地址

void func(int *a,int *b)

{.....}

参数 int *a,int *b; 是指针的形式,因此a、b中只能存储地址值

3、关于返回值

int calculate(int a,int b)
{
    int c;
    c=a+b;
    return c;
}

函数体中可以用 return语句返回一个值。那么return后面那个量的类型是什么,函数的返回值类型就设计成什么。

4、传递一维数组

void func(int a[],int n)

{.......}

void func(int *b,int n)

{.......}

这两种写法等价。或者说,前者的本质是后者,前者符合人们日常思维习惯,后者符合计算机计算思维。

5、传递二维数组

#define N 100

void func(int a[][N],int n)

{.......}

void func(int *b[100],int n)

{.......}

这两种写法等价。或者说,前者的本质是后者,前者符合人们日常思维习惯,后者符合计算机计算思维。

6、传递结构体

struct node

{

int a;

int b;

};

void func(struct node mynode){.....}

void func(struct node *mynode){.....}

结构体可以定义复杂类型,其和函数的结合,解释同普通变量

五、函数指针

本部分目的:基于函数指针,介绍C语言中快速排序函数的用法。

因此是一石二鸟,既介绍了函数指针,也介绍了快速排序函数。

在此基础上如果你能想到,啊,原理函数指针能够增加函数设计的通用性,有利于代码复用,那你是非常有悟性的。

qsort:quick sort,快速排序。

其出世之日,电闪雷鸣,震惊了世界,其他排序算法的速度不能望其项背。

1、项目驱动

对于一个整数数组,请分别对其中数据进行升序和降序排列。

2、函数原型

•#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 *) 这是函数的原型,即你必须按照这种个数来自己定义该函数。

3、比较函数的写法

int Asending(const void *x,const void *y)
{//升序比较函数 
	int *a,*b;
	a=(int*)x;
	b=(int*)y;
	if(*a>*b)
		return 1;
	else if(*a==*b)
		return 0;
	else
		return -1;	
}

int Desending(const void *x,const void *y)
{//升序比较函数 
	int *a, *b;
	a=(int*)x;
	b=(int*)y;
	if(*a>*b)
		return -1;
	else if(*a==*b)
		return 0;
	else
		return 1;	
}

注意:比较函数的名字无所谓,但是其参数和返回值类型是系统规定好的。你必须符合规则。

4、主函数

void printArray1D(int a[],int n) 
{//打印数组中数据 
	int i;
	for(i=0;i<n;i++)
	{
		printf("%d ",a[i]);
	}
	printf("\n");
}
int main()
{
	int a[]={1,4,6,2,5,3};//数组
	int n=sizeof(a)/sizeof(int);//数据个数
	
	//升序排列
	qsort(a,n,sizeof(int),Asending);	
	printArray1D(a,n) ;
	 //降序排列
	qsort(a,n,sizeof(int),Desending);	
	printArray1D(a,n) ;
	
	
	return 0;
}

注意:主调函数在调用qsort时,把比较函数当作参数传递给qsort,其内部机制为:

x会指向a[i],y会指向a[j]

若compare(x,y)返回值为1,则交换a[i]和a[j]的值,否则不交换。

所以你调整比较函数的返回值是 1 还是 -1,就能调整是升序还是降序。

5、对复杂类型数据排序

当数据不是简单数据类型时,比如对平面上的点排序,先按照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) 

猜你喜欢

转载自blog.csdn.net/weixin_43917370/article/details/107118962