您好,登錄后才能下訂單哦!
本篇內(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ù)負責完成某項特定任務(wù),提供了對過程的封裝和對細節(jié)的隱藏,這樣的代碼通常會被集成為軟件庫。
特點:
具備相對的獨立性一般有輸入值和返回值功能單一且靈活
函數(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ù)返回值例子拓展
char * strcpy ( char * destination, const char * source);
當然這里 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));
void * ( void * ptr, int value, size_t num );
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));
這樣的話只能把整型變量中每一個字節(jié)都變成1,而若想用此法置零則是可行的。
就按照這樣的方式去讀網(wǎng)站上對函數(shù)的解析內(nèi)容。
注意
每次使用庫函數(shù)都有引用#include頭文件
定義
庫函數(shù)雖好,但不可貪杯哦~ 庫函數(shù)雖多,但是畢竟不能實現(xiàn)所有功能,所以還是需要自定義函數(shù)來滿足我們的各種各樣的需求。自定義函數(shù)和庫函數(shù)一樣,有函數(shù)名、返回類型和函數(shù)參屬,但不同的是這些都由我們自己來設(shè)計。
形式
ret_type fun_name(para1,...) { statment;//語句項 } ret_type//返回類型 fun_name//函數(shù)名 para//參數(shù)
有了這樣的形式模板,我們就可以照葫蘆畫瓢了。
找出兩個數(shù)的最大值
如圖所示,寫函數(shù),函數(shù)名、參數(shù)、返回類型都要對應(yīng)。
先看再程序設(shè)計中如何進行兩數(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ā)生變化。
由圖可知,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; }
如果覺得掌握了的話,可以看看這個經(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語言不錯,那么請拿這題考考他。
代碼是從前往后執(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的源文件編譯之后,會分別生成.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ù)遞歸呢?
程序自身調(diào)用自身的編程技巧叫遞歸。
特點
大型復雜問題層層轉(zhuǎn)化為小規(guī)模的問題少量程序描述除多次運算
遞歸的思維方法在于:大事化小。
接收一個無符號整型值,按照順序打印其每一位。如輸入: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ù)打印。如:
我們發(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); }
具體流程可參考下面這張圖。
紅線部分即在返回的時候,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ù)棧幀
在第一個問題中,如果我們要去試驗的話,編譯器會報出這樣的錯誤:
Stackoverflow
(棧溢出)。
內(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)用一次就在棧上開辟一次空間,遞歸返回時,空間會被回收
不創(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é)也不同
遞歸、迭代的區(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ù)具體需求。
求n的階乘
int fac(int n) { if (n <= 1) return 1; else return n * fac(n - 1); }
求第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ì)量的實用文章!
免責聲明:本站發(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)容。