溫馨提示×

溫馨提示×

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

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

C語言中函數(shù)的介紹及用法

發(fā)布時間:2021-08-03 17:23:49 來源:億速云 閱讀:125 作者:chen 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“C語言中函數(shù)的介紹及用法”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

目錄
  • 函數(shù)

    • 定義

  • 庫函數(shù)

    • 定義

    • 介紹

    • Example 1 strcpy

    • Example 2 memset

  • 自定義函數(shù)

    • Example 1

    • Example 2 兩數(shù)交換

    • 鏈式訪問

    • Example 1

    • 函數(shù)聲明

  • 函數(shù)遞歸

    • Example 1

    • Example 2

    • 函數(shù)迭代

    • Example 3

    • Example 4


函數(shù)

定義

程序里的函數(shù)又被叫做子程序,他作為一個大型程序的部分代碼,有一或多個語句項組成。函數(shù)負責完成某項特定任務(wù),提供了對過程的封裝和對細節(jié)的隱藏,這樣的代碼通常會被集成為軟件庫。

特點:

具備相對的獨立性一般有輸入值和返回值功能單一且靈活

函數(shù)的分類有:庫函數(shù)和自定義函數(shù)。

庫函數(shù)

定義

庫函數(shù),顧名思義,放在庫里供他人使用的函數(shù)。如打印輸出這樣的基礎(chǔ)功能,他不是業(yè)務(wù)性的代碼,在開發(fā)過程中使用率高且可移植性強,故C語言的基礎(chǔ)庫里提供了這樣的一系列基礎(chǔ)功能的代碼。

一般庫函數(shù)有:

IO函數(shù)(input&output)—— printf scanf getchar putchar …字符串操作函數(shù) —— strlen strcmp strcat strcpy …字符操作函數(shù) —— tolower toupper …內(nèi)存操作函數(shù) —— memcpy menset memmove memcmp …時間/日期操作函數(shù) —— time …數(shù)學函數(shù) —— sqrt abs fabs pow …其他庫函數(shù)

介紹

為了掌握庫函數(shù)的使用方法的學習,我們可以參照權(quán)威網(wǎng)站 cplusplus 的解析為樣本,一般在不同的平臺上也是大同小異。一般都是按照這樣的順序?qū)瘮?shù)進行解析。

函數(shù)的基本信息功能描述函數(shù)參數(shù)返回值例子拓展

Example 1 strcpy

char * strcpy ( char * destination, const char * source);

C語言中函數(shù)的介紹及用法

C語言中函數(shù)的介紹及用法

當然這里 strcpy 函數(shù)的返回值是目標空間的首地址,故接收是也可以使用函數(shù)的返回值。
char arr1[20] = { 0 };
char arr2[] = "damn it!";
//1.    
char* ret = strcpy(arr1, arr2);
printf("%s\n", ret);
//2.
printf("%s\n",strcpy(arr1, arr2));

Example 2 memset

void * ( void * ptr, int value, size_t num );

C語言中函數(shù)的介紹及用法

	char arr[20] = "damn it!";
	memset(arr, 'x', 2);
	//1.
	printf("%s\n", arr);
	//2.
	printf("%s\n", (char*)memset(arr, 'x', 2));

memset函數(shù)是以字節(jié)為單位,去修改我們的地址中的內(nèi)容。

	int arr[30] = { 0 };
	memset(arr, 1, 5 * sizeof(int));

C語言中函數(shù)的介紹及用法

這樣的話只能把整型變量中每一個字節(jié)都變成1,而若想用此法置零則是可行的。

就按照這樣的方式去讀網(wǎng)站上對函數(shù)的解析內(nèi)容。

注意

  • 每次使用庫函數(shù)都有引用#include頭文件

自定義函數(shù)

定義

庫函數(shù)雖好,但不可貪杯哦~ 庫函數(shù)雖多,但是畢竟不能實現(xiàn)所有功能,所以還是需要自定義函數(shù)來滿足我們的各種各樣的需求。自定義函數(shù)和庫函數(shù)一樣,有函數(shù)名、返回類型和函數(shù)參屬,但不同的是這些都由我們自己來設(shè)計。

形式

ret_type fun_name(para1,...)
{
    statment;//語句項
}
ret_type//返回類型
fun_name//函數(shù)名
para//參數(shù)

有了這樣的形式模板,我們就可以照葫蘆畫瓢了。

Example 1

找出兩個數(shù)的最大值

C語言中函數(shù)的介紹及用法

如圖所示,寫函數(shù),函數(shù)名、參數(shù)、返回類型都要對應(yīng)。

Example 2 兩數(shù)交換

先看再程序設(shè)計中如何進行兩數(shù)交換,用醬油、醋和空瓶舉例。

C語言中函數(shù)的介紹及用法

先把a賦值給t,那么現(xiàn)在t里面存有a的值現(xiàn)在再把b賦值給a,這樣a還在t里不會被覆蓋最后把t(里的a)賦值給b,這樣就完成了a和b的互換。
void Swap1(int x, int y) {
	int t = 0;
	t = x;
	x = y;
	y = t;
}
void Swap2(int* px, int* py){
	int t = 0;
	t = *px;
	*px = *py;
	*py = t;
}
int main(){
	int a = 10;
	int b = 20;
    Swap1(a,b);
	printf("Swap1:a=%d,b=%d\n", a, b);
	Swap2(&a, &b);
	printf("Swap2:a=%d,b=%d\n", a, b);
	return 0;
}

Swap1和Swap2那個函數(shù)能夠?qū)崿F(xiàn)這樣的功能呢?

Swap1僅僅是把a和b傳值給x和y,此時去修改x和y是影響不到a和b的。

Swap2是把a,b的地址傳給指針變量px和py,這樣的話,再函數(shù)內(nèi)去將px和py解引用再修改,就可以指向a,b的內(nèi)容了。簡而言之,通過指針指向?qū)崊⒌牡刂肥沟眯螀⑴c實參同時發(fā)生變化。

C語言中函數(shù)的介紹及用法

由圖可知,px和py內(nèi)部存儲的是變量a和b的地址,這樣對px和py解引用就可以修改a和b的值。

由右圖的監(jiān)視可看出,Swap1函數(shù)x和y確實發(fā)生了交換,但并沒有影響到a和b,Swap2函數(shù)的px、py和&a、&b是一個意思。

參數(shù)

函數(shù)參數(shù)分為實際參數(shù)和形式參數(shù)兩種,

實際參數(shù)又叫實參,實參可以是任意有確定值的形式,以便在進行函數(shù)調(diào)用時,將其傳給形參。形式參數(shù)又叫形參,只有當函數(shù)調(diào)用時,他們才被分配確定值以及內(nèi)存單元,調(diào)前不存在,調(diào)后銷毀,所以形參只是形式上存在。

函數(shù)調(diào)用

1.傳值調(diào)用

形參實例化之后相當于實參的一份臨時拷貝,并且形參和實參占用不同的內(nèi)存單元,本質(zhì)上是兩個不同的變量,形參的修改不影響實參。

2.傳址調(diào)用

將外部變量的地址傳給函數(shù)參數(shù),這樣的調(diào)用可使函數(shù)內(nèi)外建立真正的聯(lián)系,即形參實參建立聯(lián)系。

練習

1.寫一個函數(shù)能夠判斷素數(shù)

#include <math.h>
int is_prime(int n)
{
	//試除法
	int j = 0;
	for (j = 2; j <= sqrt(n); j++)
	{
		if (n % j == 0)
			return 0;	
	}
	return 1;
}

函數(shù)的功能要單一且靈活,判斷素數(shù)就是判斷素數(shù),打印的操作留給其他函數(shù),這樣的話,寫出來的代碼才能夠很好的互相配合。

2.寫一個函數(shù)判斷一年是否為閏年

//是閏年返回1,不是閏年返回0
int is_leap_year(int y)
{
	return (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0));
}

不可以將兩個或者的條件分開成if…else…的形式,這樣的話1200,1600,2000這樣可整除400,也可整除100的數(shù)據(jù)就在第一輪判斷就淘汰了,進入不了第二個條件的判斷。

3.寫一個函數(shù)實現(xiàn)整型有序數(shù)組的二分查找

int binary_search(char arr[], int k, int sz)
{
	int left = 0;
	int right = sz - 1;	
	while (left <= right)
    {
		int mid = (left+right)/2;
		if (arr[mid] > k)
        {
			right = mid - 1;
		}
		else if (arr[mid] < k)
        {
			left = mid + 1;
  		}
		else
			return mid;
	}
	return -1;
}

而在主程序中是這樣的

int main()
{
	char arr[20] = { 1,2,3,4,5,6,7,8,9,10 };
	int key = 7;
    int sz = sizeof(arr) / sizeof(arr[0]);
	//計算數(shù)組元素個數(shù)
	int ret = binary_search(arr, key, sz);//TDD - 測試驅(qū)動開發(fā)
	//找到返回下標0~9
	//找不到返回-1	
    if (ret == -1)
        printf("找不到\n");
	else
        printf("找到了,下標為%d", ret);
	return 0;
}

在主程序編寫代碼時,把binary_search函數(shù)當成庫函數(shù)一樣寫,并將函數(shù)的實現(xiàn)邏輯實現(xiàn)規(guī)定好,最后再去寫函數(shù)實現(xiàn)。

這樣的方法叫TDD(test drive develop)—測試驅(qū)動開發(fā)。

1.寫一個函數(shù)每調(diào)用一次,就將num的值加1

int Add(int num)
{
	num++;
}
int main()
{
	int num = 0;
	num = Add(num);
	return 0;
}

講到這里基本內(nèi)容就講完了,下面開始進一步的深入。

嵌套調(diào)用

函數(shù)可不可以嵌套定義?

當然是不可以的,函數(shù)與函數(shù)是平等的,是并列關(guān)系,不可以在任意函數(shù)(包括主函數(shù))中定義其他函數(shù)。

但是函數(shù)是可以互相調(diào)用的。

void fun1()
{
    printf("hanpidiaoyong\n");
}
void fun2()
{
    fun1();
}
int main()
{
    fun2();
    return 0;
}

如代碼所示,main函數(shù)調(diào)用fun2函數(shù),fun2函數(shù)又調(diào)用fun1函數(shù),最終在屏幕上打印憨批調(diào)用字樣/[doge]。

鏈式訪問

鏈式訪問(chain access),顧名思義,把一個函數(shù)的返回值作為另一個函數(shù)的參數(shù)。像是用鏈子把函數(shù)首尾相連拴起來。

如:

int main()
{
    printf("%d\n",strlen("abcde")); //把strlen的返回值作為printf的參數(shù)   
    return 0;
}
int main()
{
	char arr1[20] = "xxxxxxx";
	char arr2[20] = "abcde";
	//strcpy(arr1,arr2);
	printf("%s\n", strcpy(arr1, arr2));//strcpy函數(shù)的返回值是目標空間首元素地址
	return 0;
}

Example 1

如果覺得掌握了的話,可以看看這個經(jīng)典例子。

	printf("%d", printf("%d", printf("%d", 43)));

請問這條語句輸出什么?

想要知道這個,那必然要先了解 printf 函數(shù)的返回值是什么,通過MSDN或者cplusplus.com網(wǎng)站去查找。

可以看到 printf 函數(shù)的返回值是打印字符的個數(shù),如果發(fā)生錯誤,則返回負值。(ps:scanf的返回值是輸出字符的個數(shù))

首先可以看出第三個printf打印了43;

然后第二個printf打印了第三個printf的返回值為2;

最后第一個printf打印第二個printf的返回值1;

所以屏幕上打印了4321。

筆者良心說一句,如果學校里有人說自己C語言不錯,那么請拿這題考考他。

函數(shù)聲明

代碼是從前往后執(zhí)行的,如果函數(shù)定義在后面的話,調(diào)用時便會發(fā)出警告:函數(shù)未定義,若想消除警告,我們便需要在前面聲明一下。

void test();
int main(){
	test();
}
void test()
{}

定義:聲明就是把函數(shù)定義在加個;,目的是告訴編譯器函數(shù)的返回類型、函數(shù)名、參數(shù)這些具體信息。

特點

函數(shù)的聲明一般出現(xiàn)在函數(shù)使用之前。

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

在工作的時候,一般是把函數(shù)的聲明、定義和使用放在三個不同的文件內(nèi),方便所有人協(xié)作。如:

C語言中函數(shù)的介紹及用法

鏈接:兩個.c的源文件編譯之后,會分別生成.obj的目標文件,然后再鏈接起來,最后生成.exe的可執(zhí)行文件。

在C語言,不提供源碼,也可以使用文件的內(nèi)容,怎么做的呢?

請移步至我的其他博客:關(guān)于vs2019的各種使用問題及解決方法(隨即更新)

頭文件引用

#include的預編譯指令是在預編譯階段將頭文件內(nèi)的所有內(nèi)容拷貝到源文件內(nèi)。

所以,頭文件中的內(nèi)容若是內(nèi)容重復包含則會造成效率降低。那么,怎么解決這件事呢?

  • 頭文件中包含語句#pragma once使得頭文件中不會重復包含其他頭文件;

  • 添加這樣的代碼,將語句包含起來。

#ifndef __ADD_H__// if not define 
#define __ADD_H__//define
//Add函數(shù)聲明
extern int Add(int x, int y);
#endif//end if

早期都是用第二種方法的,這兩種方法是完全等價的。

函數(shù)遞歸

什么叫函數(shù)遞歸呢?

程序自身調(diào)用自身的編程技巧叫遞歸。

特點

大型復雜問題層層轉(zhuǎn)化為小規(guī)模的問題少量程序描述除多次運算

遞歸的思維方法在于:大事化小。

Example 1

接收一個無符號整型值,按照順序打印其每一位。如輸入:1234,輸出:1 2 3 4 .

那我們創(chuàng)建一個函數(shù)叫print,若print(1234),則剝離一位變成print(123)+4,再剝離一位成print(12)+3+4,再來一位就是print(1)+2+3+4,最后只有一位了,那就全部用printf 函數(shù)打印。如:

C語言中函數(shù)的介紹及用法

我們發(fā)現(xiàn)只要將數(shù)字1234模10就可以得到4,除10便可以得到123,如此模10除10循環(huán)往復,可以將1 2 3 4全部剝離出來。

//函數(shù)遞歸
void print(size_t n)
{
	if (n > 9)//只有1位便不再往下進行
	{
		print(n / 10);
	}
	printf("%d ", n%10);
}

具體流程可參考下面這張圖。

C語言中函數(shù)的介紹及用法

紅線部分即在返回的時候,n是本次函數(shù)n,而不是前一次調(diào)用的n。

遞歸遞歸,就是遞推加回歸。

現(xiàn)在有兩個問題

  • if(n > 9)這個條件沒有行不行?沒有會怎么樣?

自然是不行的,我們在上面的推到中發(fā)現(xiàn),最后1<9條件不成立,就結(jié)束了遞歸,否則會永遠遞歸下去,造成死循環(huán)且耗干了棧區(qū)。

  • 或者是我們不論代碼的正確性,將print(n / 10)改成print(n)會怎么樣?

改成print(n)的話每次遞歸都是相同的值,遞歸也會無止境的延續(xù)下去。

這樣便引出了我們遞歸的兩個重要的必要條件:

必要條件

  • 必須存在限制條件,滿足條件時,遞歸不在繼續(xù)

  • 每次遞歸調(diào)用后必須越來越接近限制條件 函數(shù)棧幀

在第一個問題中,如果我們要去試驗的話,編譯器會報出這樣的錯誤:

C語言中函數(shù)的介紹及用法

Stackoverflow(棧溢出)。

C語言中函數(shù)的介紹及用法

內(nèi)存粗略的劃分為棧區(qū),堆區(qū),靜態(tài)區(qū)。

棧區(qū)主要存放:局部變量,形參(形參和局部變量差不多)動態(tài)內(nèi)存分配:malloc calloc等函數(shù)開辟空間靜態(tài)區(qū)主要存放:全局變量,static修飾的靜態(tài)變量

若是把棧區(qū)放大細看的話,如圖所示,有為main函數(shù)開辟的空間和print函數(shù)開辟的空間,為函數(shù)開辟的空間叫函數(shù)棧幀也可以叫運行時堆棧。

程序開始執(zhí)行時開辟空間,程序結(jié)束時銷毀空間函數(shù)每調(diào)用一次就在棧上開辟一次空間,遞歸返回時,空間會被回收

Example 2

不創(chuàng)建臨時變量,實現(xiàn)Strlen函數(shù)

int my_strlen1(char* pa)
{
    int count = 0;
	while (*pa++ != '\0')
	{//pa++;
		count++;
	}
	return count;
}
//my_strlen求字符串長度
int my_strlen(char* pa)
{
	if (*pa == 0){
		return 0;
	}
	return 1+my_strlen(pa + 1);//直接返回長度	
}

具體的思考方式呢,就是只要第一個字符不是0,那我們就在外面+1并且跳到下一個字符,直到找到‘\0',那么我們返回0。

ps:

字符指針+1,向后跳一個字節(jié)
整型指針+1,向后跳四個字節(jié)
指針+1都是向后跳一個元素的地址,指針類型不同向后跳的字節(jié)也不同

C語言中函數(shù)的介紹及用法

C語言中函數(shù)的介紹及用法

函數(shù)迭代

遞歸、迭代的區(qū)別?

遞歸是重復調(diào)用函數(shù)自身實現(xiàn)循環(huán)。

迭代是函數(shù)內(nèi)某段代碼實現(xiàn)循環(huán),循環(huán)代碼中變量既參與運算同時也保存結(jié)果,當前保存的結(jié)果作為下一次循環(huán)計算的初始值。

遞歸循環(huán)中,遇到滿足終止條件的情況時逐層返回來結(jié)束。

迭代則使用計數(shù)器結(jié)束循環(huán)。

當然很多情況都是多種循環(huán)混合采用,這要根據(jù)具體需求。

Example 3

求n的階乘

int fac(int n)
{
	if (n <= 1)
		return 1;
	else
		return n * fac(n - 1);
}

Example 4

求第n個斐波那契數(shù)

int fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return fib(n - 1) + fib(n - 2);
}

但是這個方法效率是非常低的,當數(shù)字特別大時,層層拆分下來,時間效率是 O ( 2 n ) O(2^n) O(2n)。

根據(jù)公式可知,第三個斐波那契數(shù)可由前兩個得到,我們利用這個規(guī)律

int fib(int n)
{
	int a = 1;
	int b = 1;
	int c = 1;
	while (n >= 3)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}

上一個c變成了b,上一個b變成了a。如此循環(huán)往復。

利用迭代的方式,計算一個數(shù)只需要計算n-2次,這樣的話時間復雜度就是 O ( n ) O(n) O(n)。效率大大提高。

有這兩題我們可以發(fā)現(xiàn),什么時候用遞歸簡單呢?

1.有公式有模板的時候
2.遞歸簡單,非遞歸復雜的時候
3.有明顯問題的時候

“C語言中函數(shù)的介紹及用法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向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