C語言-函數-學會方程你的數學能力會乘風破浪日新月異-學會函數你的編程能力將百尺竿頭更進一步

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

方程是一種數學工具,列方程解方程可以有效解決數學問題。面試

函數是一種編程技巧,設計函數實現函數可以提升程序設計的效率。算法

函數之因此難學,是由於它太多變了,有多種形態。編程

課本不寫全吧,做者會被罵。數組

課堂不講全吧,老師會被罵。函數

因而乎,老師滿堂灌,學生糊塗蛋。工具

學生要想不當糊塗蛋,必須拼命地訓練。學習

師傅領進門,修行在我的,毫不是推諉責任,而是事物發展的規律。優化

客官,若是您有時間,請慢慢聽我胡扯。ui

1、建議初學者,把變量定義所有寫在函數外,成爲全局變量

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

這樣理解:

一、把這個程序總體看作一個學校,a、b、c至關於書記、校長、副校長的名字;

二、main函數至關於學校的一個系;這個程序簡單,因此暫時只有一個系;

三、main函數內部至關於系的內部,系內部固然都知道校領導們的名字

怎麼樣,通俗易懂吧。

注:對於各類程序設計競賽,主要考察算法,所以整體上程序代碼不會太長,並且對效率的要求比較高,採起這種全局變量是常常性的手段。

2、代碼鬧獨立,要稱王

咱們能夠像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();//輸出
		
}

(函數調用示意圖,來自百度圖片搜索)

分析:

一、main函數的代碼都不安分,一氣化三清,代碼獨立啦,分解爲新的三個函數,input、calculate、output

二、好處是,分而治之好管理了,等你們遇到比較複雜的程序,超過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);//輸出
		
}

分析:

一、爲了達到每一個函數獨立的目的,還須要有理論的支撐。函數外定義的變量叫「全局變量」,函數內定義的變量叫「局部變量」。全局變量、局部變量各在內存的什麼地方分配才能高效?

二、mai函數的a、b和input中的a、b只是重名而已,可是他們不在同一個系,不會衝突。什麼樣的機制才能保障他們不衝突?

三、以上,說出來輕鬆,編譯程序卻要精心設計,才能應對變化。

四、最主要的,這些設計的原理,卻會成爲找工做時面試官的題目。因此在各類課本,函數不只單獨一章,還和其後各章相關聯。

所以,同志們,同窗們,在大一的階段,40個課時,只能學基本語法,就像幼兒園學個拼音、兒歌、數個數,哪能講什麼語法,講了也沒什麼效果。

C 語言背後原理,或者其餘語言背後的原理,有一門課叫作編譯原理的,會慢慢到來。

你想,一門課的東西,你非要你的C語言老師給你講清楚,臣妾估計也是打死都作不到啊。

編譯原理三大經典書籍(龍書 虎書 鯨書):

https://blog.csdn.net/shenwansangz/article/details/44217233

編譯原理三大聖書——龍書、虎書、鯨…

https://blog.csdn.net/xiaolanmyself/article/details/16944135

同窗,你骨骼不太驚奇,不建議你看,知道這三本書的名字,會吹吹牛bi得了。

若是你去看了,我怕你會「學習計算機編程,從入門到放棄」......

4、函數語法

函數返回值類型   函數名(參數列表)

{

//函數體

}

上面就是函數的語法形式。

就像是天然語言有語法同樣,程序設計語言也是有語法的。

你寫文章頭疼,編程也會頭疼。只不過編程要比寫文章簡單不少。

其中最難的是,參數列表的寫法與解釋。

無他,惟手熟爾。

只有多讀程序,多寫程序,纔可以掌握。

但是,親愛的計算機專業大學生們,每一個學期那麼多課程,怎麼會有時間寫程序呢?

無他,惟有捨得,捨棄哪些對你的人生目標不重要的科目,及格就行;把功夫放在編程上,才能獲得!

一、簡單數據傳值

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){.....}

結構體能夠定義複雜類型,其和函數的結合,解釋同普通變量

 

5、函數指針

本部分目的:基於函數指針,介紹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 *) 這是函數的原型,即你必須按照這種個數來本身定義該函數。

三、比較函數的寫法

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;	
}

注意:比較函數的名字無所謂,可是其參數和返回值類型是系統規定好的。你必須符合規則。

四、主函數

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,就能調整是升序仍是降序。

五、對複雜類型數據排序

當數據不是簡單數據類型時,好比對平面上的點排序,先按照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)