溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C語言函數(shù)使用實例分析

發(fā)布時間:2022-04-14 10:22:27 來源:億速云 閱讀:194 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了C語言函數(shù)使用實例分析的相關(guān)知識,內(nèi)容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇C語言函數(shù)使用實例分析文章都會有所收獲,下面我們一起來看看吧。

函數(shù)的聲明和定義

函數(shù)聲明

  • 告訴編譯器有一個函數(shù)叫什么,參數(shù)是什么,返回類型是什么。但是具體是不是存在,函數(shù)聲明決定不了

  • 函數(shù)的聲明一般出現(xiàn)在函數(shù)的使用之前。要滿足先聲明后使用

  • 函數(shù)的聲明一般要放在頭文件中的

函數(shù)定義

函數(shù)的定義是指函數(shù)的具體實現(xiàn),交待函數(shù)的功能實現(xiàn)

舉例

簡單的求和函數(shù)

一般寫簡單的求和函數(shù),求和功能直接寫在main( )函數(shù)了。

//簡單的求和函數(shù)
int main()
{
	int a = 10;
	int b = 20;
	int sum = a+b;
	printf("%d\n", sum);
	return 0;
}
把加法單獨改寫成函數(shù)

把加法功能單獨寫成一個函數(shù),放在主函數(shù)前面。如果將函數(shù)add放在主函數(shù)后面,則會報錯,因為程序自上而下進行的,主函數(shù)執(zhí)行后,發(fā)現(xiàn)add函數(shù)未定義,找不到。

//之前的有函數(shù)的寫法.函數(shù)放在前面
int add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int sum = add(a, b);
	printf("%d\n", sum);
	return 0;
}
添加函數(shù)聲明
int add(int x, int y);//函數(shù)的聲明

int main()
{
	int a = 10;
	int b = 20;

	int sum = add(a, b);

	printf("%d\n", sum);

	return 0;
}

int add(int x, int y)//定義放在主函數(shù)后面,需要先聲明
{
	return x + y;
}
帶頭文件和函數(shù)聲明

實際上,當函數(shù)代碼較多時,一般采用模塊化編程,每個函數(shù)實現(xiàn)功能盡量單一,函數(shù)間要低耦合、高內(nèi)聚。因此,針對上面的加單的加法函數(shù),用帶頭文件的寫法重寫一遍。

先定義源文件 test.c 、源文件 add.c和頭文件 add.h

//源文件test. c
#include "add.h"
int main()
{
	int a = 10;
	int b = 20;
	int sum = add(a, b);
	printf("%d\n", sum);
	return 0;
}
//源文件add.c
int add(int x, int y)//定義放在主函數(shù)后面,需要先聲明
{
	return x + y;
}
//頭文件add.h
int add(int x, int y);//函數(shù)的聲明
靜態(tài)庫(.lib)的生成

當編程寫了一個減法的函數(shù)給別人用,但是又不想把源碼直接分享給別人,這時候就可以將代碼編譯成靜態(tài)庫(就是.lib文件)。

靜態(tài)庫的特點:將函數(shù)編譯成靜態(tài)庫,別人可以正常使用封裝好的代碼,但是又看不到源碼。

下面舉例說明,如何生成靜態(tài)庫(.lib):

新建VS工程,新建源文件 sub.c和 頭文件 sub.h,編寫一個減法函數(shù) sub

//源文件 sub.c
int sub(int x, int y)//函數(shù)定義需要先聲明
{
	return y - x;
}
//頭文件 sub.h
int sub(int x, int y);

依次點擊解決方案資源管理器——項目名稱——右鍵選屬性,彈出對話框。

C語言函數(shù)使用實例分析

然后依次點擊——配置屬性——常規(guī)——項目默認值——配置類型——下拉菜單選擇靜態(tài)庫(.lib)——應用——確定。

C語言函數(shù)使用實例分析

接著點擊生成——生成解決方案。

C語言函數(shù)使用實例分析

最終會在工程文件夾下的——Debug文件夾——看到靜態(tài)庫.lib文件。

C語言函數(shù)使用實例分析

用記事本打開靜態(tài)庫,可以看到是亂碼。

C語言函數(shù)使用實例分析

靜態(tài)庫文件的使用方法

接下來說明如何使用別人或者自己生成好的靜態(tài)庫文件:

(1)將函數(shù)對應的同名頭文件.h文件 和 同名靜態(tài)庫.lib拷貝至自己的工程文件中。

C語言函數(shù)使用實例分析

(2)在頭文件中添加上t頭文件 sub0119.h

C語言函數(shù)使用實例分析

(3)在源文件中添加減法頭文件引用 和靜態(tài)庫的引用,

#include "add.h"//加法頭文件
#include "sub0119.h"//減法頭文件
#pragma comment(lib,"sub0119.lib")//靜態(tài)庫必須加上

(4)程序運行時,會通過上面的引用將生成的靜態(tài)庫加載進來。在主函數(shù)直接使用 減法函數(shù)sub即可。

//帶頭文件的寫法
int main()
{
	int a = 10;
	int b = 20;
	int sum = add(a, b);//一般的函數(shù)調(diào)用
	int subnum = sub(a, b);//使用靜態(tài)庫
	printf("%d\n", sum);
	printf("%d\n", subnum);
	return 0;
}

運行程序見下圖:

C語言函數(shù)使用實例分析

函數(shù)遞歸

什么是遞歸?

  • 遞歸做為一種算法在程序設(shè)計語言中廣泛應用。

  • 一個過程或函數(shù)在其定義或說明中有直接或間接調(diào)用自身的一種方法

  • 它通常把一個大型復雜的問題層層轉(zhuǎn)化為一個與原問題相似的規(guī)模較小的問題來求解

  • 只需少量的程序就可描述出解題過程所需要的多次重復計算,大大地減少了程序的代碼量

  • 遞歸的主要思考方式在于:把大事化小,將問題轉(zhuǎn)化為可以重復有限次的小問題解決

遞歸的兩個必要條件

  • 存在限制條件,當滿足這個限制條件的時候,遞歸便不再繼續(xù)

  • 每次遞歸調(diào)用之后越來越接近這個限制條件

練習1

接受一個整型值(無符號),按照順序打印它的每一位。

輸入:1234,輸出 1 2 3 4

一般方法
void print(num)//自定義打印函數(shù)
{
	int arr[10] = { 0 };//定義數(shù)組
	for (int i = 0; i < 4; i++)
	{//將數(shù)字存放在數(shù)組里
		arr[i] = num % 10;//取數(shù)字最后一位
		num = num / 10;//取整數(shù)
	}
	for (int i = 3; i >= 0; i--)
	{//倒著打印
		printf("%d ", arr[i]);
	}
}
int main()
{
	int num = 1234;
	print(num);
	return 0;
}
遞歸的方法

分析:打印1234可以分解成下圖那樣拆解,分別把不同位上的數(shù)字取出,最終把數(shù)字拆解剩最后一位時,開始打?。?/p>

  • print(1234),數(shù)字大于9表明數(shù)字還不是個位數(shù),于是將1234拆解成123 和 4,分別通過取余和取模操作。把取余的123再次傳給函數(shù)print

  • print(123),數(shù)字大于9表明數(shù)字還不是個位數(shù),于是將123拆解成12 和 3,分別通過取余和取模操作,把取余的12再次傳給函數(shù)print

  • print(12),數(shù)字大于9表明數(shù)字還不是個位數(shù),于是將12拆解成1 和 2,分別通過取余和取模操作,把取余的1再次傳給函數(shù)print

  • print(1),數(shù)字小于9表明數(shù)字是個位數(shù),也就是分解到最后一步了,這就是遞歸的限制條件,于是將1取模操作,打印出來

C語言函數(shù)使用實例分析

代碼如下所示:

void print(int num)
{
	if (num>9)
	{
		print(num/10);	//取余	
	}
	printf("%d ",num % 10);//取模
}
int main()
{
	int num = 1234;
	print(num);

	return 0;
}

通過調(diào)試,我們分析整個遞歸程序的運行邏輯見下圖,紅色圓圈的1 2 3 4表示程序的執(zhí)行順序:

  • 第一次調(diào)用函數(shù)print,此時 num=1234, n>9, 滿足if條件, 執(zhí)行print(123), 調(diào)用函數(shù)print自身

  • 第二次調(diào)用函數(shù)print,此時 num=123, n>9, 滿足if條件, 執(zhí)行print(12), 調(diào)用函數(shù)print自身

  • 第三次調(diào)用函數(shù)print,此時 num=12, n>9, 滿足if條件, 執(zhí)行print(1), 調(diào)用函數(shù)print自身

  • 第四次調(diào)用函數(shù)print,此時 num=1, n<9, 不滿足if條件, 執(zhí)行printf(“%d”, num%10), 此時num=1, 打印1

此時函數(shù)已經(jīng)拆解到最后一層了,函數(shù)執(zhí)行結(jié)束返回到上一次調(diào)用的地方,綠色圓圈的1 2 3 4表示程序的返回順序:

  • 在綠色圓圈1處,函數(shù)printf打印1后,將會返回到上一次調(diào)用print的地方,即綠色圓圈2

  • 在綠色圓圈2處,if語句已經(jīng)執(zhí)行完畢,按順序執(zhí)行printf(“%d”, num%10),此時num=12, 打印2。接著將會返回到上一次調(diào)用print的地方,即綠色圓圈3處

  • 在綠色圓圈3處,if語句已經(jīng)執(zhí)行完畢,按順序執(zhí)行printf(“%d”, num%10),此時num=123, 打印3。接著將會返回到上一次調(diào)用print的地方,即綠色圓圈4處

  • 在綠色圓圈4處,if語句已經(jīng)執(zhí)行完畢,按順序執(zhí)行printf(“%d”, num%10),此時num=1234, 打印4。接著將會返回到上一次調(diào)用print的地方,即主函數(shù)main中。

在函數(shù)運行過程中,每調(diào)用一次函數(shù),在內(nèi)存棧中就會開辟空間存放num的值,如下面藍色方框顯示,第一次調(diào)用函數(shù)存放的1234 在最底層, 以此類推,1是最后調(diào)用函數(shù)存放的,就在最上面。

在函數(shù)返回時,看綠色圓圈 1 2 3 4, num的值就從上向下取值,

  • 在綠色圓圈1處,num數(shù)值為1。打印1后,函數(shù)運行結(jié)束,存放1的空間就銷毀了

  • 此時返回到綠色圓圈2里,num數(shù)值為12,打印2后,函數(shù)運行結(jié)束,存放12的空間也銷毀了

  • 此時返回到綠色圓圈3里,num數(shù)值為123,打印3后,函數(shù)運行結(jié)束,存放123的空間也銷毀了

  • 此時返回到綠色圓圈2里,num數(shù)值為1234,打印4后,函數(shù)運行結(jié)束,存放1234的空間也銷毀了

C語言函數(shù)使用實例分析

運行結(jié)果見下圖:

C語言函數(shù)使用實例分析

練習2

編寫函數(shù)不允許創(chuàng)建臨時變量,求取字符串的長度

一般方法
//編寫函數(shù)不允許創(chuàng)建臨時變量,求取字符串的長度
void getlen(char* arr)
{
	int count = 0;//計數(shù)
	while (*arr!='\0')
	{
		count++;//計數(shù)加1
		arr++;//地址移動到下一個字符
	}
	printf("%d", count);

}
int main()
{
	char arr[] = "abcd";
	getlen(arr);//數(shù)組名就是首元素地址
	return 0;
}
遞歸方法

分析:自定義函數(shù)getlen,計算字符串 abcd,字符串以 '\0&rsquo;結(jié)尾,這是隱藏的,實際字符串為"abcd\0"自定義函數(shù)可以分解成下圖那樣拆解,每次取出一個字符,最終把字符都取完,返回值:

  • getlen(abcd),判斷字符是不是&rsquo;\0&rsquo;, 不是,于是將getlen(abcd) 拆解成 1+ getlen(bcd)

  • getlen(bcd), 判斷字符是不是&rsquo;\0&rsquo;, 不是,于是將getlen(bcd) 拆解成 1+ getlen(cd)

  • getlen(cd), 判斷字符是不是&rsquo;\0&rsquo;, 不是,于是將getlen(cd) 拆解成 1+ getlen(d)

  • getlen(d), 判斷字符是不是&rsquo;\0&rsquo;, 不是,于是將getlen(d) 拆解成 1+ getlen(&rsquo;\0&rsquo;),已經(jīng)到字符串結(jié)尾了

  • getlen(&rsquo;\0&rsquo;), 判斷字符是不是&rsquo;\0&rsquo;, 是,于是返回值0,代表字符串結(jié)束了,這就是遞歸的限制條件

C語言函數(shù)使用實例分析

下面是實現(xiàn)代碼:

//遞歸方法
int getlen(char* arr)
{
	if (*arr != 0)
	{
		arr++;
		return 1 + getlen(arr);
	}
	return 0;//等于0,字符串結(jié)束了,返回0
}
int main()
{
	char arr[] = "abcd";
	int sz = getlen(arr);
	printf("%d", sz);
	return 0;
}

程序運行結(jié)果見下圖,紅色路線是遞歸按順序調(diào)用函數(shù),綠色的路線是遞歸達到限制條件后,返回值

C語言函數(shù)使用實例分析

C語言函數(shù)使用實例分析

練習3

求n的階乘。(不考慮溢出)

一般方法
//求n的階乘
void fact(int n)
{
	int num = 1;
	for (int i = 1; i <= n; i++)
	{
		num = num * i;
	}
	printf("%d", num);
}
int main()
{
	fact(3);//階乘
	return 0;
}
遞歸方法

分析:自定義函數(shù)fact,求n的階乘。自定義函數(shù)可以分解成下圖那樣拆解:

  • fact(n)=n!=n*(n-1)&hellip;1=nfact(n-1),

  • fact(n-1)=(n-1)!=(n-1)&hellip;*1=(n-1)*fact(n-2),

  • 依次類推, fact(2)=2!=2*fact(1),

  • 判斷n已經(jīng)推到1了,返回fact(1) =1,這就是遞歸的限制條件。

C語言函數(shù)使用實例分析

//遞歸方法
int fact(int n)
{
	if (n >= 1)
	{
		return n*fact(n - 1);
		n--;
	}
	else
	{
		return 1;
	}
	
}
int main()
{
	int num=fact(3);//階乘
	printf("%d", num);
	return 0;
}

運行結(jié)果見下圖:

C語言函數(shù)使用實例分析

練習4

求第n個斐波那契數(shù)。(不考慮溢出)

一般方法
//一般方法
void fib(int n)
{
	int num1 = 1;
	int num2 = 1;
	int num3 = 0;
	for (int i = 1; i <=(n-2); i++)
	{
		num3 = num1 + num2;
		num1 = num2;
		num2 = num3;
	}
	printf("%d", num3);//輸出5
}
int main()
{
	fib(5);//第五個斐波那契數(shù)列是5
	return 0;
}
遞歸方法

分析:自定義函數(shù)fib,求第n個斐波那契數(shù)列。自定義函數(shù)可以分解成下圖那樣拆解:

  • fib(1)=1,fib(2)=1

  • fib(3)=fib(2)+fib(1),fib(4)=fib(3)+fib(2),

  • 依次類推, fib(n)=fib(n-1)+fib(n-2),

  • 求fib(6),就往前遞推,fib(6)=fib(5)+fib(4)

  • 一直推到 fib(3)=fib(2)+fib(1), fib(1),fib(2)為已知值,數(shù)列推到此結(jié)束了,直接給返回值就行, 這就是遞歸的限制條件。

C語言函數(shù)使用實例分析

//遞歸方法方法
int fib(int n)
{
	if (n <= 2)
	{
		return 1;
	}
	return fib(n - 1) + fib(n - 2);
}
int main()
{
	int num=fib(5);
	printf("%d", num);
	return 0;
}

遞歸與迭代

遞歸隱藏的問題

在前面7.2.2.4 小節(jié)練習4中,發(fā)現(xiàn)有一個問題,舉例fib(6)說明,下面是計算fib(6)時用的遞歸方法,分析會發(fā)現(xiàn)fib(3)居然重復計算了3次,如果計算fib(40)時,這樣的重復計算會更多,大量的重復計算勢必會降低計算速度。

C語言函數(shù)使用實例分析

通過程序來驗證一樣,計算fib(6)時,fib(3)總共計算了幾次:

int count = 0;//全局變量
int fib(int n)
{
	if (n == 3)
		count++;//計算fib(3)計算了多少次
	if (n <= 2)
		return 1;
	return fib(n - 1) + fib(n - 2);
}
int main()
{
	int num=fib(6);
	printf("%d\n", num);
	printf("%d\n", count);
	return 0;
}

結(jié)果如下所示:fib(6) = 8,fib(3)總共計算了3次:**

C語言函數(shù)使用實例分析

當計算fib(40)時,fib(3)總共計算了幾次?

結(jié)果見下圖,fib(40) = 102334155,fib(3)總共計算了39088169次,驚呆了居然3千多萬次。而且很耗時間,計算效率低。

C語言函數(shù)使用實例分析

而用一般的方法計算fib(40)時,fib(3)只計算了一次。

如何改進
  • 在調(diào)試 factorial 函數(shù)的時候,如果你的參數(shù)比較大,那就會報錯: stack overflow(棧溢出)這樣的信息。例如計算fib(50)時,結(jié)果為負數(shù),就是溢出了。

C語言函數(shù)使用實例分析

  • 系統(tǒng)分配給程序的棧空間是有限的,但是如果出現(xiàn)了死循環(huán),或者(死遞歸),這樣有可能導致一

直開辟棧空間,最終產(chǎn)生??臻g耗盡的情況,這樣的現(xiàn)象我們稱為棧溢出。

那如何解決上述的問題:

  • 將遞歸改寫成非遞歸。例如計算fib(40)時,就采用一般方法計算。

  • 使用static對象替代 nonstatic 局部對象。 在遞歸函數(shù)設(shè)計中,可以使用 static 對象替代 nonstatic 局部對象(即棧對象),這不僅可以減少每次遞歸調(diào)用和返回時產(chǎn)生和釋放 nonstatic 對象的開銷,而且 static 對象還可以保存遞歸調(diào)用的中間狀態(tài),并且可為各個調(diào)用層所訪問。

選遞歸還是迭代
  • 許多問題是以遞歸的形式進行解釋的,這只是因為它比非遞歸的形式更為清晰。

  • 但是這些問題的迭代實現(xiàn)往往比遞歸實現(xiàn)效率更高,雖然代碼的可讀性稍微差些。

  • 當一個問題相當復雜,難以用迭代實現(xiàn)時,此時遞歸實現(xiàn)的簡潔性便可以補償它所帶來的運行時開銷。

  • 當發(fā)現(xiàn)遞歸的算法很耗時間,都沒結(jié)果出來,那可能要考慮迭代實現(xiàn)了。

關(guān)于“C語言函數(shù)使用實例分析”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“C語言函數(shù)使用實例分析”知識都有一定的了解,大家如果還想學習更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI