日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

初级程序员面试不靠谱指南(六)

發(fā)布時間:2023/12/31 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 初级程序员面试不靠谱指南(六) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

五.很強很偉大的函數(shù)指針

??? 我想看到這個標(biāo)題中“函數(shù)指針”幾個字之后,估計有一半人會選擇關(guān)掉界面,因為我最開始學(xué)習(xí)C語言的時候這一章我曾無數(shù)次跳過,看到書中那些復(fù)雜的星號括號直接就崩潰了,加上老師自己本身也講不清楚,所以學(xué)習(xí)興趣大減。但是到后面,當(dāng)我意識到函數(shù)指針的牛逼和偉大之后,我不禁開始認真的思考并學(xué)習(xí)了這部分內(nèi)容,絕對受益匪淺。如果你想了解很多編程的技巧以及C++的面向?qū)ο笫侨绾螛?gòu)造出來的,我建議你應(yīng)該好好學(xué)習(xí)函數(shù)指針,我也會分兩或者三篇來介紹這個知識,特別是在后面,我將會簡單的展示下用c語言如何能做到C++多態(tài)等面向?qū)ο蟮奶卣?#xff0c;這樣當(dāng)你遇到面試時有人問:"new和malloc有什么區(qū)別"的時候,你再也不必百度出答案以后照本宣科了。

??? 函數(shù)指針絕對是C/C++語言中比較讓人惡心的東西之一,面對著眼花繚亂的*和(),很多人直接就跪了,面試的時候經(jīng)常會遇到函數(shù)指針和指針函數(shù)有啥區(qū)別這樣的問題,從這兩個名字和中國人造詞的方法就可以看出一二,函數(shù)指針本質(zhì)是一個指針,指針函數(shù)本質(zhì)是一個函數(shù),雖然這句話沒什么意義,但是作為一個指導(dǎo)思想可以讓你在理解的時候可以把握一個大方向。

1.函數(shù)指針的"函數(shù)"。函數(shù)指針既然叫這個名字,那么就分別從函數(shù)和指針兩個方面來介紹一下好了。為了不至于看到這里就有30%的人關(guān)掉界面,首先先從簡單的開始,在c語言里面聲明一個函數(shù)是很簡答的一件事,你只要遵循[返回類型][函數(shù)名稱][參數(shù)列表]這三大部分進行聲明,你就可以聲明出一個函數(shù),比如int f()。這個函數(shù)返回類型是int ,函數(shù)名稱是f,參數(shù)列表為空。為了文章能扯的下去,我們先定義一個返回值為int*的函數(shù)開始,也就是int *f()。

???? 在這個之前,先扯點看起來稍微遠一點的知識好了。在c/c++中,!運算符是一個單目運算符,就是說其所需的變量為一個,這個運算符的含義是“邏輯非”,也就是true變成false,false變成true。比如:!b,就表示對b這個變量取反,是不是感覺很弱智了?那么好,你需要理解的是在函數(shù)調(diào)用的"()"也是一個運算符,不僅僅是在四則運算中采用(),用點裝逼的語句就是,這個括號要廣義的理解。也就是當(dāng)你調(diào)用一個函數(shù)的時候,你可以理解為這也是在做一種運算,這種運算的調(diào)用方法就是中使用()符號。再通俗一點,如果我在某一個函數(shù)中使用f()調(diào)用一個函數(shù),這樣也就是我采用這樣一個運算符來進行一種計算,這種“計算”是調(diào)用函數(shù),雖然()并不是一種單目運算符,但是為了這個問題更加簡單,采用這樣一種形式,目的是想強調(diào)()也是一種運算符,盡管長的很不像。

????? 將()看作運算符的一個重要意義就是,運算符至少都是有優(yōu)先級和結(jié)合性的,而更加不利于理解的一個伏筆就是()的優(yōu)先級除了在[]運算符之下之外,是優(yōu)先級最高的一個運算符,更加通俗的一個解釋就是,如果有()運算符那么就要先計算這個運算符,這也就是為什么函數(shù)指針的聲明形式看起來那么別扭的原因。比如下面這個例子int *f(),將()看作一個“函數(shù)調(diào)用”運算,那么就是先進行“函數(shù)調(diào)用”運算,然后在進行“取地址運算”,通俗的說法是,這是一個函數(shù),返回一個int類型的指針。那么如果我采用括號改變這兩個運算符的優(yōu)先級,變成int (*f)(),那么這個過程就變成了,先進行“取地址運算”,再進行“函數(shù)調(diào)用”運算,也就是取出這個地址上的變量,再進行函數(shù)調(diào)用,通俗的說法就是,這是一個地址,地址上靜靜的矗立著一個函數(shù)。

????? 上面的是不是有點繞?那讓我們先暫時忘記上面的內(nèi)容,仔細來看一下int (*f)(),這是不是一個函數(shù)?根據(jù)函數(shù)的定義,這很明顯不是一個函數(shù),因為你不能把函數(shù)的名稱設(shè)置為(*f),因此,你也不能這樣寫int (*f)(){},因為這不是函數(shù),沒有函數(shù)體,你所做的只是聲明一個指針。這里的()符號都是運算符,在這一個式子里面具有不同的意義。你可以這樣來看待這樣一個奇怪的結(jié)構(gòu),由于括號可以改變運算符的優(yōu)先級,所以首先這是一個指針,就像(5+3)*7,首先是計算加法一樣。然后將int ()看做一個部分,看做是一個"函數(shù)調(diào)用"運算,這個指針指向的是這樣的一個函數(shù)調(diào)用,它具有的特點是返回值是int,無參數(shù)列表,就像int *b,這是一個指針,指向int類型的數(shù)據(jù)。這里請停下來思考1分鐘,然后再試試你能不能分辨出int *b[2]和int (*b)[2]的區(qū)別,如果不能,請再思考1分鐘,如此往復(fù)知道思考明白為止。

2.函數(shù)指針的"指針"。如果能看到這里,你已經(jīng)戰(zhàn)勝了至少15%的人了。既然函數(shù)指針本質(zhì)是一個指針,那么就從指針的角度再來看看這玩意兒。如何在C語言里面聲明一個指針,我想是任何一個看過超過50頁c語言的人都能回答的問題,比如說int *f。這個概念絕大多數(shù)人都能很容易的理解,所以我們將這個概念嫁接到函數(shù)指針這個概念上,相對于整型指針,你可以把函數(shù)指針理解為指向函數(shù)的指針,就像整型指針的用法是int b=0;int *f=&b;一樣,遞推出聲明一個函數(shù)指針以后你也可以像這樣做類似的操作。你可以做這樣的嘗試,定義一個函數(shù)fpointed,然后類似普通指針的用法那樣*f=fpointed。

????? 你會發(fā)現(xiàn),如果你運氣比較好的話,可以通過編譯,但是我相信絕大數(shù)情況下,你會接到報錯的消息,不過至少你領(lǐng)悟到了一個道理,和所有整數(shù),浮點數(shù)等等一樣,函數(shù)在程序中也是有一個地址的,雖然你不知道這是怎樣一個形式,但是根據(jù)指針指向的是一個地址的基本原則,你至少應(yīng)該記住這一個概念才不至于太驚訝。為什么函數(shù)指針不能隨便指向一個函數(shù)呢?只是因為"函數(shù)"和"整數(shù)"這兩個概念是不同的,雖然都帶有"數(shù)",但是就像不是所有的鳥都能飛一樣,不是所有的帶有數(shù)的東西都是一類。單從外形上判斷,你能說int f1(int a)和int f2()是一種東西嗎?先不管其他的,前面的比后面的長一大截呢,人都需要用不同的形式進行記錄,更不用說編譯器,所以不存在一種“通用”的函數(shù)指針能指向所有函數(shù),這就涉及到函數(shù)指針和函數(shù)之間的關(guān)系問題。

????? 觀察一下f1和f2,對于一個函數(shù)什么最不重要?應(yīng)該是名字,f1同樣可以叫f2,你要是喜歡叫他f22222222都可以,決定它不同于其他某類函數(shù)的是它的返回值和它所包含的參數(shù)列表,就像人一樣,你叫什么名字并不重要,重要的是你給別人表現(xiàn)出來的能力和你自己所本身包含的涵養(yǎng),這是你區(qū)別于別人并且立于世上的基本。這種情況下,回到1里面說過的,對于一個函數(shù)指針,你可以分成兩個部分來看待它,將int 和()看做指向的部分,(*f)看做指針的部分,如果你想聲明指向一個“返回值為int并且?guī)в幸粋€int參數(shù)的函數(shù)”的指針,應(yīng)該怎么做。這時候你應(yīng)該大膽嘗試,(*f)這個不能變,因為這已經(jīng)是一個指針,只是沒有明確指向什么,那么按照描述,你要寫出指向的部分應(yīng)該int (int a),根據(jù)函數(shù)的聲明中形參的部分,你應(yīng)該可以猜到這個a是可以省去的,將這兩個部分拼起來,就可以得到這樣一個東西int (*f)(int ),這就是指向一個“返回值為int并且?guī)в幸粋€int參數(shù)的函數(shù)”的指針,換句話說,你可以用指向f1的地址,也就是f=&f1,到這里,你可以認為自己已經(jīng)會聲明函數(shù)指針了。

????? 好了,和上面一樣,先暫停1分鐘,思考一下如何聲明出指向一個“返回值為int*并且?guī)в袃蓚€int參數(shù)的函數(shù)”的指針。

????? 既然聲明好了,那么怎么使用這個東西呢?還是和上面一樣,如果你定義了這樣一個函數(shù):

int fPointed(int x){printf("pointed %d\r\n",x); }

????? 如果你想在main中調(diào)用該函數(shù),你會使用fPointed(1)之類的語句去調(diào)用。那么如果聲明了一個函數(shù)指針并指向它,就像下面這樣,

int (*f)(int ); f=&fPointed;

????? 怎樣通過這個函數(shù)指針去調(diào)用這個函數(shù)呢?回想一下普通指針是如何使用的,比如int a=0;int *b=&a;如果你想通過b來取到a內(nèi)存中所保存的數(shù),你會采用*b這樣的方式,同理,你想去的f里面所指向的函數(shù),同理應(yīng)該使用*f這樣的方式,只是函數(shù)指針畢竟指向的是一個函數(shù),你需要給編譯器一個"函數(shù)調(diào)用"的運算符,并給與正確的"運算變量"(正確的參數(shù)類型及個數(shù)),所以完整的調(diào)用方式應(yīng)該如下:

(*f)(2);

????? 至此,你已經(jīng)可以使用函數(shù)指針代替函數(shù)來進行調(diào)用活動了,此處,如果你是第一次看到這些東西并完成想明白上面的內(nèi)容,應(yīng)充滿了成就感。

3.函數(shù)指針第一次應(yīng)用。函數(shù)指針的應(yīng)用實在是太廣泛了,并且?guī)淼姆奖阈院颓擅钚越^對是可以令人鼓掌的,和上面的方法一樣,誰的第一次都不容易,所以先從簡單的開始,比如,你想做一個可以進行有"加減乘除"四則運算的小程序,這個程序可以根據(jù)你輸入的內(nèi)容來選擇不同的算法,不管你信不信,這是我研究生入學(xué)復(fù)試的第一題,當(dāng)時覺得太弱智了,現(xiàn)在想想,就是這種題目你一樣可以讓別人看到你的與眾不同,所以千萬別小看任何一個問題。最一般的程序,也是90%的人寫的程序一定是下面這樣的:

float Plus (float a, float b) { return a+b; } float Minus (float a, float b) { return a-b; } float Multiply(float a, float b) { return a*b; } float Divide (float a, float b) { return a/b; }float Cal(float a, float b, char opCode) {float result;switch(opCode){case '+' : result = Plus (a, b); break;case '-' : result = Minus (a, b); break;case '*' : result = Multiply (a, b); break;case '/' : result = Divide (a, b); break;}return result; }

???? 然后在main里面,通過傳入不同的Code來標(biāo)示自己想進行的運算,比如Cal(1.0,2.0,‘+’);最后會得到3.0。這個思路就不用多介紹了吧?這還是只有四則運算,你只需寫4個case語句就可以了,如果要有40個不同類型的運算怎么辦?這樣進行維護成本太高。而這問題使用函數(shù)指針可以很好的去掉switch從而解決這個問題。

??? 首先根據(jù)上面的定義,并且觀察這四則運算,發(fā)現(xiàn)返回值都是float,參數(shù)都是(float,float),所以你需要的是一個指向"返回值為float并且?guī)в袃蓚€float參數(shù)的函數(shù)“指針,很容易寫出來是float (*f)(float,float)。

??? 接著,想想看如何替換掉這個switch語句呢?你可以順著這條路思考,如果我能夠直接傳入一個函數(shù),而不用進行判斷再選擇函數(shù)這樣就不用switch了。如何將函數(shù)"傳入"函數(shù),這里面需要你再一次從腦海中想起函數(shù)指針是一個指針的概念,既然是一個指針,那么就可以作為一個形式參數(shù)放在一個函數(shù)的參數(shù)列表里,就像int f(int *b)一樣,同樣,我們可以講函數(shù)指針作為一個函數(shù)的參數(shù),只不過看起來更加別扭而已,不管怎么樣,我們可以采用下面這樣的一個函數(shù)去掉switch語句:

float Cal2(float a, float b, float (*f)(float, float)) {float result = f(a, b); return float; }

???? 在調(diào)用的時候可以直接Cal2(1.0,2.0,&Plus),傳入相應(yīng)的函數(shù)地址計算結(jié)果,這樣就不用維護一個龐大的switch結(jié)構(gòu),只需要在調(diào)用端傳入相應(yīng)的函數(shù)就可以了。當(dāng)然這也有一個弊端,就是只能傳入返回值為float并且?guī)в袃蓚€float參數(shù)的函數(shù)。

4.函數(shù)指針應(yīng)用one more time。這一個應(yīng)用不僅僅是為了展示函數(shù)指針的用處,更是為了展示程序作為一個工具其實和數(shù)學(xué)是親密的。很多人一看到用程序?qū)崿F(xiàn)某某算法就頭大,直接放棄的概率絕對大于50%,雖然這個例子很簡單,但是我很想傳達一個思想,就是計算機的本質(zhì)是運算,運算絕對離不開算法,所以某種角度上說算法是程序的核心之一,也是學(xué)寫程序的一個本質(zhì)目標(biāo)之一。

????? 我在學(xué)高等數(shù)學(xué)的時候曾想過如何用程序?qū)懛e分運算?無奈那時候水平有限,想破頭也想不出來,因為積分運算參與運算的不僅有數(shù),還有函數(shù),那時候傳數(shù)容易,怎么傳函數(shù)真是蛋疼了。不過現(xiàn)在根據(jù)上面的思路,你可以很容易的想出解決辦法,就是傳入一個函數(shù)指針,如果你對積分已經(jīng)忘了,你可以百度一下相關(guān)知識,我也只記得一重積分的運算方法了,所以我也只寫了一個計算一重積分的例子。

????? 稍微回顧一下一重積分的運算方法,可以想起來的是,一重積分有一個上限,一個下限,然后有一個積分變量(已經(jīng)忘了是不是學(xué)名叫這個了),其幾何意義就是這個積分變量的曲線,在上限和下限在曲線上的取值向x軸做垂線,然后這兩條垂線,曲線以及x軸圍成的面積就是這個積分的值。由于曲線不能像計算長方形面積那樣長乘以寬,所以要采用特殊的方法,這個特殊并且精彩絕倫的方法就是將這個區(qū)域分成很多寬度很小的近似長方形,分別計算這些長方形的面積,然后將他們加起來,這個長方形的寬度接近無限小的時候,這個面積就是積分的值。唉,以上是我的全部記憶,如果有錯誤,請大聲的指出來。這個思路不難,就是取兩個點的函數(shù)的值,用一個很小的變量作為寬度,為了更加精確,我采用的計算梯形的面積,然后將這些面積加起來。你可以將寬度取不同的值,你會發(fā)現(xiàn)取的越小,最后結(jié)果越精確,但也不能太小,畢竟計算機里的數(shù)都是有精度的。我相信,如果畫個圖,你就會頓時明白了,這篇太長了,我下篇再稍微仔細介紹一下這段程序的思路好了,先把代碼貼出來:

float Calculus(float (*f)(float),int start,int end) {float range=end-start;float delta=0.01;float loopIndex=0.0;float sum=0.0;while(range-loopIndex>0.000000001){sum+=(f(loopIndex-delta)+f(loopIndex))*delta/2;loopIndex+=delta;}return sum; }float X2(float x) {return pow(x,2); }float X3(float x) {return pow(x,3); }int main(int argc, char *argv[]) {printf("%f\r\n",Calculus(&X3,0,3));//計算x^3在0-3之間的積分值return 0; }

???? 下面super_boy的這句話我覺得對理解這個概念會有幫助,所以我附在后面了~

???? “函數(shù)在調(diào)用的時候,都會去維護一個棧的平衡,也就是在調(diào)用之初,進行壓棧,調(diào)用結(jié)束的時候進行出棧。而由于函數(shù)的參數(shù)的不同,就導(dǎo)致壓棧和出棧的次數(shù)不同。如果在申明的時候不把參數(shù)個數(shù),和類型傳給函數(shù)指針的話,就沒法保證在運行的時候棧的平衡。程序也就崩潰了。”

??

?????

?

轉(zhuǎn)載于:https://www.cnblogs.com/ZXYloveFR/p/3146235.html

總結(jié)

以上是生活随笔為你收集整理的初级程序员面试不靠谱指南(六)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。