摘要:語言在設(shè)計(jì)中考慮了函數(shù)的高效性和易用性兩個(gè)原則。在語言中,最常見的當(dāng)屬函數(shù)了。以上就是一個(gè)函數(shù),它被稱為語言的入口函數(shù),或者主函數(shù)。例如和都是函數(shù)名。形式參數(shù)當(dāng)函數(shù)調(diào)用完成之后就自動(dòng)銷毀了。
函數(shù)可以把大的計(jì)算任務(wù)分解成若干較小的任務(wù),然后通過調(diào)用的方式達(dá)到代碼復(fù)用;一個(gè)邏輯不寫多遍,減少代碼維護(hù)成本。
調(diào)用函數(shù)的一方不需要了解函數(shù)的具體實(shí)現(xiàn),對于它來說,這部分是一個(gè)“黑盒子”,從而使得程序結(jié)構(gòu)更加清晰。
C語言在設(shè)計(jì)中考慮了函數(shù)的 高效性 和 **易用性 **兩個(gè)原則。
函數(shù)的實(shí)現(xiàn)應(yīng)該盡量簡短,因?yàn)楹瘮?shù)可以套函數(shù),一個(gè)程序應(yīng)該盡量由許多小的函數(shù)組成,而不是少量較大的函數(shù)組成。
在刷題的過程中,系統(tǒng)會(huì)給我們事先提供一個(gè)函數(shù)讓我們來實(shí)現(xiàn),而它則是調(diào)用函數(shù)的一方。
在C語言中,最常見的當(dāng)屬main
函數(shù)了。
int main(){ printf("5201314/n"); return 0;}
以上就是一個(gè)函數(shù),它被稱為C語言的 入口函數(shù),或者 主函數(shù)。
所有程序執(zhí)行都是從這個(gè)函數(shù)開始的,以它為例,我們引出函數(shù)的一些基本概念。
如圖:
通過這個(gè)圖,我們類比main()
這個(gè)函數(shù),它的:
返回類型 是int
32位整型,
函數(shù)名 為main
,
參數(shù)列表 為空
,
函數(shù)體 為 printf("5201314/n");
,
返回值 為0
。
函數(shù)的返回類型可以是任意類型;
例如:整型int
,浮點(diǎn)型float
,字符型char
,自定義類型等待;
返回類型 和 返回值 是配套的,當(dāng)返回類型為void
時(shí),函數(shù)內(nèi)部的返回值可以寫return
;也可以省略不寫。
函數(shù)名可以類比我們自己的名字。是給函數(shù)調(diào)用方用的。
例如:main
和printf
都是函數(shù)名。
函數(shù)的參數(shù)列表必須用()
括起來,參數(shù)是函數(shù)需要處理的數(shù)據(jù);
例如:
printf("hello/n");
這一段代碼用來輸出字符串,"hello/n"
就是一個(gè)參數(shù),參數(shù)類型是字符串。
下面會(huì)詳細(xì)解釋:printf
;
函數(shù)內(nèi)部就是你可以任意發(fā)揮的部分,也就是函數(shù)的核心邏輯部分,可以是各種語句的組合;當(dāng)然也可以是另一個(gè)函數(shù),也就是函數(shù)說,函數(shù)是支持嵌套的。
函數(shù)的返回值則表示:這個(gè)函數(shù)最后返回給調(diào)用方的數(shù)據(jù);
如果返回值的類型和函數(shù)的返回類型不一致,則會(huì)進(jìn)行強(qiáng)制類型轉(zhuǎn)換;前提是能夠強(qiáng)制轉(zhuǎn)換的情況下。
為什么會(huì)有庫函數(shù)?
我們學(xué)習(xí)C語言編程的時(shí)候,總是在一個(gè)代碼編寫完成之后迫不及待的想知道結(jié)果, 把這個(gè)結(jié)果打印到我們的屏幕上看看。這個(gè)時(shí)候我們會(huì)頻繁的使用一個(gè)功能:將信息按照一定的格式打印到屏幕上(printf
)。
在編程的過程中我們會(huì)頻繁的做一些字符串的拷貝工作(strcpy
)。
編程是我們也計(jì)算,總是會(huì)計(jì)算n的k次方這樣的運(yùn)算(pow
)
像上面我們描述的基礎(chǔ)功能,它們不是業(yè)務(wù)性的代碼。
我們在開發(fā)的過程中每個(gè)程序員都可能用的到, 為了支持可移植性和提高程序的效率,所以C語言的基礎(chǔ)庫中提供了一系列類似的庫函數(shù),方便程序員進(jìn)行軟件開發(fā)。
這里推薦一個(gè)網(wǎng)站:cplusplus.com
簡單的總結(jié),C語言中,我們常用的庫函數(shù)都有:
IO函數(shù):輸入和輸出函數(shù)
字符串操作函數(shù):比如求字符串長度的strlen
函數(shù)
字符操作函數(shù):比如判斷字符的大小寫,將小寫字母轉(zhuǎn)化為大寫字母
內(nèi)存操作函數(shù):內(nèi)存復(fù)制、查找等操作
時(shí)間/日期函數(shù):獲取時(shí)間等
數(shù)學(xué)函數(shù):開平方sqrt
等函數(shù)
等等…
我們參照文檔,學(xué)習(xí)幾個(gè)庫函數(shù);
strcpy
從上面的庫函數(shù)網(wǎng)站可以找到strcpy的具體用法:
其實(shí)strcpy
就是字符串拷貝的意思
因此在使用這個(gè)函數(shù)的時(shí)候,我們需要向函數(shù)參數(shù)傳入destination(目標(biāo)數(shù)組)和source(源數(shù)組)兩個(gè)字符串,同時(shí)該函數(shù)的返回值是char*
,它會(huì)將拷貝后的destination(目標(biāo)數(shù)組)起始地址返回給我們。
strcpy
時(shí)要引用頭文件#include
官方寫法:
char* strcpy(char* strDestination, const char* strSource);
即第一個(gè)參數(shù)是目的地地址,第二個(gè)參數(shù)源地址;
代碼示例:
#include #include int main(){ char arr1[] = "Fighting Boy"; char arr2[] = "AAA"; strcpy(arr1, arr2);//將arr2數(shù)組的內(nèi)容拷貝到arr1數(shù)組中 printf("arr1 = %s/n", arr1);//arr1 = "AAA" printf("arr2 = %s/n", arr2); return 0;}
運(yùn)行結(jié)果:
注意:
這里的復(fù)制其實(shí)并不是真正的復(fù)制,準(zhǔn)確的說是覆蓋!
即把arr2的所有內(nèi)容(包括/0
)都覆蓋到arr1
對應(yīng)位置;
也就是說,雖然打印出來arr1
是AAA
,但是實(shí)質(zhì)上arr1
等于AAA/0ting Boy
;
我們可以運(yùn)行調(diào)試一下:
補(bǔ)充:
/0
結(jié)束。如果源字符串沒有/0
,就會(huì)一直拷貝源字符串地址后面的所有內(nèi)容,直到找到值為0
。/0
拷貝到目標(biāo)數(shù)組中。const
修飾(關(guān)于const
的用法后續(xù)會(huì)寫一篇博客詳解)
注意: 使用庫函數(shù),必須包含 #include
對應(yīng)的頭文件。
如果庫函數(shù)能干所有的事情,那還要程序員干什么?
所有更加重要的是自定義函數(shù)。
自定義函數(shù)和庫函數(shù)一樣,有函數(shù)名,返回值類型和函數(shù)參數(shù)。
但是不一樣的是這些都是我們自己來設(shè)計(jì)。
自定義函數(shù)的定義語法為:
ret_type fun_name(para1, *){ statement;//語句項(xiàng) return 返回值;//該返回值與返回類型必須相同,如果是void型函數(shù),則不需要返回值,因此可以不寫。}ret_type 返回類型fun_name 函數(shù)名para1 函數(shù)參數(shù)
在使用函數(shù)的過程中,我們用函數(shù)名(函數(shù)參數(shù))
的形式來調(diào)用自定義函數(shù);
由于一般函數(shù)在調(diào)用完以后產(chǎn)生一個(gè)返回值(比如一個(gè)兩個(gè)數(shù)相加的加法函數(shù),實(shí)現(xiàn)兩個(gè)數(shù)相加以后返回兩個(gè)數(shù)的和),因此我們可以用一個(gè)變量來接收這個(gè)返回值。
例題:寫一個(gè)函數(shù)可以找出兩個(gè)整數(shù)中的最大值。
代碼示例:
int get_max(int x, int y){ return (x > y) ? (x) : (y); //三目操作符,在操作符中講過,如果x>y則返回x,反之則返回y;}int main(){ int a = 20; int b = 10; int max = get_max(a, b);//定義一個(gè)max變量,用來接受最大值; printf("max=%d/n", max);}
運(yùn)行結(jié)果:
在程序運(yùn)行過程中,給get_max
這個(gè)函數(shù)傳入a、b
這兩個(gè)參數(shù),函數(shù)調(diào)用完后會(huì)返回a和b中的最大值;
因此可以用max
來接收這個(gè)返回值。當(dāng)然也可以不用接收,因?yàn)樵诤瘮?shù)運(yùn)行完以后,get_max(a, b)
就相當(dāng)于這個(gè)返回值,該返回值可以當(dāng)做printf
的參數(shù)直接進(jìn)行打印操作。
例題:寫一個(gè)函數(shù)可以交換兩個(gè)整形變量的內(nèi)容。
void Swap(int* pa, int* pb) //我們只需要交換a和b當(dāng)中的內(nèi)容,不需要返回值,所以用void{ int t = 0;//定義一個(gè)臨時(shí)變量用于交換 t = *pa; *pa = *pb; *pb = t;}int main(){ int a = 1; int b = 2; printf("交換前: a=%d b=%d/n", a, b); Swap(&a, &b); printf("交換前: a=%d b=%d/n", a, b); return 0;}
運(yùn)行結(jié)果:
分為:
真實(shí)傳給函數(shù)的參數(shù),叫實(shí)參。
實(shí)參可以是:常量、變量、表達(dá)式、函數(shù)等。
無論實(shí)參是何種類型的量,在進(jìn)行函數(shù)調(diào)用時(shí),它們都必須有確定的值,以便把這些值傳送給形參。
代碼示例:
void Swap1(int x, int y){ int tmp = 0; tmp = x; x = y; y = tmp;}void Swap2(int* px, int* py) //我們只需要交換a和b當(dāng)中的內(nèi)容,不需要返回值,所以用void{ int tmp = 0; tmp = *px; *px = *py; *py = tmp;}int main(){ int a = 1; int b = 2; 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;}
運(yùn)行結(jié)果:
Swap1
和Swap2
函數(shù)中的參數(shù) x,y,px,py
都是形式參數(shù)。
在main函數(shù)中傳給Swap1
的a,b
和傳給Swap2函數(shù)的&a,&b
是實(shí)際參數(shù)。
這里我們對函數(shù)的實(shí)參和形參進(jìn)行分析:
可以看出:
實(shí)參a、b與形參x、y不是同一空間
&a:0x00d3fa28&b:0x00d3fa1c &x:0x00def944&y:0x00d3f948
這里可以看到Swap1
函數(shù)在調(diào)用的時(shí)候,x,y
擁有自己的空間,同時(shí)擁有了和實(shí)參一模一樣的內(nèi)容。
簡單來說:形參實(shí)例化之后其實(shí)相當(dāng)于實(shí)參的一份臨時(shí)拷貝。
分為:
傳值調(diào)用
傳址調(diào)用
例題:寫一個(gè)函數(shù)來實(shí)現(xiàn)對兩個(gè)數(shù)的交換
void Swap1(int x, int y){ int tmp = 0; tmp = x; x = y; y = tmp;}int main(){ int a = 1; int b = 2; Swap1(a, b); printf("Swap1: a=%d b=%d/n", a, b); return 0;}
運(yùn)行結(jié)果:
由于形參并不影響實(shí)參,函數(shù)在調(diào)用過程中只是對形參x,y進(jìn)行了交換,并沒有影響到a,b;
并且函數(shù)在調(diào)用結(jié)束以后,x,y就已經(jīng)被銷毀了。
調(diào)試看一下:
從監(jiān)視中就可以看出,x,y
的地址和a,b
不同,他們相當(dāng)于一塊獨(dú)立的空間,自身的改變并不會(huì)影響a,b
。
還是看這個(gè)例題,只不過我們稍微做點(diǎn)改變
void Swap2(int* px, int* py){ int tmp = 0; tmp = *px; *px = *py; *py = tmp;}int main(){ int a = 1; int b = 2; Swap2(&a, &b); printf("Swap2: a=%d b=%d/n", a, b); return 0;}
運(yùn)行結(jié)果:
通過這種方式可以使得變量進(jìn)行真正的交換。
還是調(diào)試看一下:
通過監(jiān)視可以看出,px、py
就是一個(gè)指針,其中放的就是a,b
的地址,*
是解引用操作符,它可以通過地址找到地址中存放的變量值;
比如px
就是a
的指針,它的值是a的地址,對px
解引用就可以找到a地址中存放的變量1,然后我們就可以對變量1進(jìn)行操作了。
函數(shù)內(nèi)部的形參只需要借用函數(shù)外部實(shí)參的值的時(shí)候用傳值調(diào)用,比如求兩個(gè)數(shù)的較大值。
當(dāng)函數(shù)內(nèi)部需要對函數(shù)外部變量進(jìn)行操作時(shí)用傳址調(diào)用,比如交換兩個(gè)數(shù)。
關(guān)于函數(shù)的形參、實(shí)參和傳值、傳址,可以看我之前寫的這篇文章:重點(diǎn)詳解函數(shù)的形參和實(shí)參、傳值和傳址
函數(shù)和函數(shù)之間是可以互相調(diào)用的。
代碼示例:
void fun2(){ printf("hello/n");}void fun1(){ int i = 0; for (i = 0; i < 3; i++) { fun2(); }}int main(){ fun1(); return 0;}
運(yùn)行結(jié)果:
我們通過main
函數(shù)調(diào)用fun1
,通過fun1
調(diào)用三次fun2
,這就是函數(shù)的嵌套調(diào)用。
注意:函數(shù)可以嵌套調(diào)用,但是不能嵌套定義。
把一個(gè)函數(shù)的返回值作為另外一個(gè)函數(shù)的參數(shù)。
int get_max(int x, int y){ return (x > y) ? x : y;}int main(){ int num1 = 10; int num2 = 20; int max = get_max(num1, num2); printf("max=%d/n", get_max(num1, num2));}
運(yùn)行結(jié)果:
下面我們看一個(gè)有趣的代碼:
int main(){ printf("%d", printf("%d", printf("%d", 43))); //結(jié)果是啥? //注:printf函數(shù)的返回值是打印在屏幕上字符的個(gè)數(shù) return 0;}
運(yùn)行結(jié)果:
這個(gè)程序?qū)嶋H上就是用printf
的返回值作為printf
的參數(shù);
因此想要弄明白這個(gè)程序,我們得先知道printf
的返回值;
通過查找printf的用法可以知道printf
返回值:
所以printf
返回值是寫入的字符總數(shù),也就是字符的個(gè)數(shù)。
最內(nèi)層的printf
打印43;
第二層的printf
打印的是最內(nèi)層printf的返回值,也就是43
這個(gè)內(nèi)容的元素個(gè)數(shù)2;
最外層打印的是printf("%d",2)
的返回值,返回值是其元素個(gè)數(shù)1;
所以會(huì)打印出4321
這四個(gè)數(shù)。
函數(shù)的定義是指函數(shù)的具體實(shí)現(xiàn),交待函數(shù)的功能實(shí)現(xiàn)。
test.h
的內(nèi)容
#ifndef __TEST_H__#define __TEST_H__//函數(shù)的聲明int Add(int x, int y);
test.c
的內(nèi)容
#include "test.h"//函數(shù)Add的實(shí)現(xiàn)int Add(int x, int y){ return x + y;}
這種分文件的書寫形式,在后期寫三字棋和掃雷的時(shí)候,就可以用分模塊來寫。
我之前寫過一篇三子棋的小游戲,就是用這種分模塊的方式來寫的;
游戲鏈接:升級版三子棋小游戲(棋盤界面美化+動(dòng)畫演示)
這里我們通過代碼來詳解函數(shù)的遞歸
接受一個(gè)整型值(無符號),按照順序打印它的每一位。
例如: 輸入:1234,輸出 1 2 3 4.
思考一下:
我們想要從高位向低位輸出,就必須要依次獲取:最高位到最低位的數(shù)字;
因此我們可以這1234
每次除以10,并把1234 / 10
的結(jié)果進(jìn)行判斷,看其是否小于10;
如果小于10則不再進(jìn)行除以10的操作,這樣我們就可以得到最高位1
了;
為了獲取其他位的數(shù)字,我們可以在每次除以10之前進(jìn)行取模10的操作,得到剩下的位。
代碼示例:
void print(int n){ if (n > 9) { print(n / 10); } printf("%d ", n % 10);}int main(){ int num = 1234; print(num); //這里的print是我們自己定義的函數(shù),不是庫函數(shù)printf return 0;}
運(yùn)行結(jié)果:
關(guān)于這道題,我之前也做過詳解,并且畫了圖,可以去看下:深入理解C語言中函數(shù)的遞歸算法
有些問題是以遞歸的形式進(jìn)行解釋的,這只是因?yàn)樗确沁f歸的形式更為清晰。
但是這些問題的迭代實(shí)現(xiàn)往往比遞歸實(shí)現(xiàn)效率更高,雖然代碼的可讀性稍微差些。
當(dāng)一個(gè)問題相當(dāng)復(fù)雜,難以用迭代實(shí)現(xiàn)時(shí),此時(shí)遞歸實(shí)現(xiàn)的簡潔性便可以補(bǔ)償它所帶來的運(yùn)行時(shí)開銷
其實(shí)后面關(guān)于函數(shù)的嵌套、遞歸、迭代都只是寫了一些知識點(diǎn),所以還是打算通過例題的方式來詳解。
更多的一些重點(diǎn)函數(shù)(比如strlen
,strcmp
,strcat
等)的實(shí)現(xiàn),我會(huì)在之后多帶帶拿出來說明。
函數(shù)這一章的很多東西都需要練題+深刻理解,為后續(xù)的知識打下基礎(chǔ)!
等例題剖析完了,會(huì)附上鏈接!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/124776.html
摘要:二什么是文件磁盤上的文件就是文件。文件指針變量定義是一個(gè)指向類型數(shù)據(jù)的指針變量。表示向何種流中輸出,可以是標(biāo)準(zhǔn)輸出流,也可以是文件流。文件結(jié)構(gòu)體指針,將要讀取的文件流。 ...
摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報(bào)率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級技術(shù)專家我看過哪些技術(shù)類書籍。 大家好,我是...
目錄 一、什么是C語言? 二、第一個(gè)C語言程序 代碼 程序分析 ?程序運(yùn)行 一個(gè)工程中出現(xiàn)兩個(gè)及以上的main函數(shù) 代碼 運(yùn)行結(jié)果 分析 三、數(shù)據(jù)類型 數(shù)據(jù)各種類型 為什么會(huì)有這么多的數(shù)據(jù)類型? 計(jì)算機(jī)單位 ?各個(gè)數(shù)據(jù)類型的大小 ?注意事項(xiàng) 數(shù)據(jù)類型的使用 四、變量和常量 變量的分類 變量的使用 變量的作用域和生命周期 ?常量 五、字符串+轉(zhuǎn)義字符+注釋 字符串 ?轉(zhuǎn)義字符 注釋 六、選擇語句 ?...
閱讀 1743·2023-04-26 01:02
閱讀 4921·2021-11-24 09:39
閱讀 1838·2019-08-30 15:44
閱讀 2935·2019-08-30 11:10
閱讀 1810·2019-08-30 10:49
閱讀 1016·2019-08-29 17:06
閱讀 635·2019-08-29 16:15
閱讀 925·2019-08-29 15:17