最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

c語(yǔ)言中形參與實(shí)參的關(guān)系解讀

 更新時(shí)間:2023年07月25日 15:25:36   作者:Royel transformed  
這篇文章主要介紹了c語(yǔ)言中形參與實(shí)參的關(guān)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

c語(yǔ)言中形參與實(shí)參的關(guān)系

一、形參出現(xiàn)在函數(shù)定義中,在整個(gè)函數(shù)體內(nèi)都可以使用,形參變量只有在被調(diào)用時(shí)才分配內(nèi)存單元,在調(diào)用結(jié)束時(shí)即刻釋放所分配的內(nèi)存單元,因此,形參只有在函數(shù)內(nèi)部有效。 函數(shù)調(diào)用結(jié)束返回主調(diào)函數(shù)后則不能再使用該形參變量。  離開(kāi)該函數(shù)則不能使用,實(shí)參出現(xiàn)在主調(diào)函數(shù)中,進(jìn)入被調(diào)函數(shù)后,實(shí)參變量也不能使用。 

二、形參和實(shí)參的功能是作數(shù)據(jù)傳送。發(fā)生函數(shù)調(diào)用時(shí), 主調(diào)函數(shù)把實(shí)參的值傳送給被調(diào)函數(shù)的形參從而實(shí)現(xiàn)主調(diào)函數(shù)向被調(diào)函數(shù)的數(shù)據(jù)傳送。

三、實(shí)參可以是常量、變量、表達(dá)式、函數(shù)等, 無(wú)論實(shí)參是何種類型的量,在進(jìn)行函數(shù)調(diào)用時(shí),它們都必須具有確定的值, 以便把這些值傳送給形參。因此應(yīng)預(yù)先用賦值,輸入等辦法使實(shí)參獲得確定值。

四、實(shí)參和形參在數(shù)量上,類型上,順序上應(yīng)嚴(yán)格一致, 否則會(huì)發(fā)生“類型不匹配”的錯(cuò)誤。 

五、實(shí)參和形參的數(shù)據(jù)傳遞方式有兩種,

1.一種是值傳遞,實(shí)參和形參都不是指針,這種情況下函數(shù)調(diào)用中發(fā)生的數(shù)據(jù)傳送是單向的。 即只能把實(shí)參的值傳送給形參,而不能把形參的值反向地傳送給實(shí)參。 在函數(shù)調(diào)用過(guò)程中,形參的值發(fā)生改變,而實(shí)參中的值不會(huì)變化。究其原因,是因?yàn)檫@兩個(gè)參數(shù)在內(nèi)存中位于不同的位置,值傳遞只是形參將實(shí)參的內(nèi)容復(fù)制,在內(nèi)存中被分配了新的內(nèi)存單元,在函數(shù)執(zhí)行完畢以后地址會(huì)立刻被釋放掉,因此形參的改變不會(huì)對(duì)實(shí)參有任何影響。

2.另一種是地址傳遞,就是實(shí)參與形參共用同一個(gè)內(nèi)存單元,在函數(shù)執(zhí)行的過(guò)程中,實(shí)際就是對(duì)實(shí)參的地址進(jìn)行操作,因此形參改變,實(shí)參同步變化,實(shí)際他們就是同一變量,因?yàn)樵趦?nèi)存中占據(jù)的就是一個(gè)內(nèi)存單元。

不過(guò)下面這個(gè)例子很狡猾,就是指針作為形參時(shí),實(shí)參*s只是把地址傳遞給了形參*p,在函數(shù)體內(nèi),p指針指向了下一個(gè)內(nèi)存地址,因此原內(nèi)存地址中的內(nèi)容并未改變,p指針變量的如何變化與S無(wú)關(guān),除非p指針改變了實(shí)參所指向位置的內(nèi)存單元中的數(shù)值,那么訪問(wèn)實(shí)參所得值也會(huì)隨之變化。

void func(char *p)
{
? ? p=p+1;
}
int main()
{
? ? char s[]={'1','2','3','4'};
? ? func(s);
? ? printf("%c",*s);
? ? return 0;
}

輸出結(jié)果為1

并不是2

形參與實(shí)參在系統(tǒng)堆棧的變化過(guò)程

在各種計(jì)算機(jī)編程語(yǔ)言中,都少不了函數(shù)調(diào)用來(lái)完成某些特定功能。我們要使用一個(gè)函數(shù)前,必定會(huì)先定義該函數(shù)(可能還會(huì)先聲明它)。在這個(gè)過(guò)程中,我們大多會(huì)給這個(gè)函數(shù)傳遞某些‘參數(shù)’,當(dāng)我們定義被調(diào)用函數(shù)時(shí),函數(shù)名后的括號(hào)( )中即為我們傳入的“形參”,函數(shù)可以在函數(shù)體中描述如何操作這些“形參”;同時(shí),我們調(diào)用函數(shù)時(shí),例如 fun(param1,param2,...),括號(hào)里面的這些param,我們稱之為實(shí)參。

簡(jiǎn)單來(lái)說(shuō),調(diào)用函數(shù)時(shí)可以把實(shí)參傳給定義的函數(shù)作為形參以供函數(shù)來(lái)使用它們。

形參和實(shí)參的定義確實(shí)很好理解,但如果僅僅是理解它們是什么的話,這往往是不夠的。

接下來(lái),我會(huì)在系統(tǒng)堆棧層面說(shuō)明一下函數(shù)調(diào)用過(guò)程中,形參與實(shí)參以及函數(shù)局部變量是如何在堆棧里變化的。

先說(shuō)一個(gè)C語(yǔ)言中的例子,如果我們想要通過(guò)一個(gè)子函數(shù)交換在主函數(shù)main()里面定義的兩個(gè)局部變量x,y的值的話,一些初學(xué)者可能會(huì)像這樣寫code:

 

寫完之后,在主函數(shù)打印了一下x,y發(fā)現(xiàn)x與y的值根本沒(méi)有變化??!

在這里,我們?cè)谥骱瘮?shù)中定義了兩個(gè)int類型的局部變量x和y,它們的值分別為3和5,然后我們調(diào)用了在主函數(shù)前面定義的交換函數(shù)Exchange,并將變量x與y分別作為了兩個(gè)實(shí)參傳遞給它;再看Exchange函數(shù)中,我們的得到了兩個(gè)傳過(guò)來(lái)的形參a、b,然后我們通過(guò)同一個(gè)中間變量使它們(注意,兩個(gè)形參)交換。得到的結(jié)果時(shí),主函數(shù)的x,y的值并沒(méi)有發(fā)生交換。

這個(gè)代碼正確的寫法應(yīng)該是:

這次我們的實(shí)參是&x,&y,即傳遞過(guò)去的是x,y的地址,相當(dāng)傳遞過(guò)程中存在int *a=&x與int *b=&y操作,其結(jié)果是讓a指向x,b指向y。然后同樣通過(guò)tmp完成值交換。

如果我們單單從指針?lè)矫嫒タ紤]為什么這次能夠交換成功,可能還不太容易弄懂交換的本質(zhì),接下來(lái)讓我談一談這次交換在系統(tǒng)堆棧中是如何做到的。

眾所周知,計(jì)算機(jī)存儲(chǔ)體系分為4層,它們的讀寫速度從慢到快依次是外存<內(nèi)存<高速緩存<CPU寄存器。

其中,寄存器又可分類為通用寄存器,段寄存器,輔助寄存器,兩個(gè)特殊寄存器:IP寄存器和Flag標(biāo)志位寄存器

所有的C源程序都要進(jìn)行編譯,從而生成最終的機(jī)器指令代碼和文件,那么,上述的內(nèi)容最終是由編譯軟件負(fù)責(zé)完成的;也就是說(shuō),編譯軟件將我們的ASCII碼形式的源程序代碼,根據(jù)自己的原則,使用上述的寄存器完成源代碼的任務(wù)。反過(guò)來(lái)說(shuō),如果在編程中需要特殊處理,那么,就需要給編譯器一定的“指導(dǎo)”,方可達(dá)到我們的目的!

輔助寄存器中有兩個(gè)寄存器bp(基址寄存器)和sp (棧寄存器),通常bp表示系統(tǒng)堆棧的棧底地址,sp表示系統(tǒng)堆棧的棧頂?shù)刂贰?/p>

特殊寄存器IP(指令寄存器),可以用來(lái)存儲(chǔ)“下一步”要執(zhí)行的某個(gè)進(jìn)程(線程)的代碼的地址的;

函數(shù)調(diào)用的過(guò)程中就發(fā)生了如下的變化:

首先,我們將代碼的執(zhí)行過(guò)程顯示成以.asm為后綴的匯編文件

 

可以看見(jiàn),當(dāng)我們調(diào)用Exchange函數(shù)時(shí),我們將x與y的地址作為實(shí)參變量傳遞了過(guò)去,在匯編層面,發(fā)生的事情是先通過(guò)lea指令將ebp[x]和ebp[y]地址的偏移量,把它們分別傳送到通用寄存器里的dx和cx寄存器里面。

然后通過(guò)call指令調(diào)用函數(shù)Exchange.也就是說(shuō),調(diào)用函數(shù)時(shí),我們將距離ebp(棧底地址)為4個(gè)和8個(gè)字節(jié)的x和y變量地址偏移量,傳送給ecx和edx寄存器;有人可能想問(wèn),上圖中為什么_x$=-8,_y$=-12。

我們都知道,堆棧是一種后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),局部變量定義后,會(huì)使堆棧內(nèi)存減小,地址往低處走,局部變量相對(duì)于棧底指針bp在其上面,也就是堆棧的低處,所以偏移量用負(fù)值表示。

同時(shí),主函數(shù)其實(shí)也是一個(gè)相對(duì)于操作系統(tǒng)的子函數(shù),操作系統(tǒng)調(diào)用主函數(shù),然后主函數(shù)再調(diào)用子函數(shù)。

也就是說(shuō),建立主函數(shù)時(shí),我們?cè)谥骱瘮?shù)中定義的相對(duì)于主函數(shù)的局部變量也同樣被壓入了保存變量空間的系統(tǒng)堆棧。接下來(lái)我將通過(guò)主函數(shù)調(diào)用子函數(shù)的例子說(shuō)明:

子函數(shù)是被call指令執(zhí)行的,call指令能夠停止主函數(shù)的運(yùn)行,并進(jìn)入子函數(shù),運(yùn)行其函數(shù)體內(nèi)容。當(dāng)然,還要保護(hù)主函數(shù)的運(yùn)行狀態(tài)信息,以使子函數(shù)調(diào)用完成返回后能夠接著執(zhí)行主函數(shù)后面的操作!進(jìn)入子函數(shù)后,先將ebp入棧,這樣做是為了保護(hù)主函數(shù)的ebp信息。第二步則是將esp移到ebp,棧頂指針和棧底指針重合,形成了一個(gè)空棧。而這個(gè)空棧就是為Exchange函數(shù)中局部變量所準(zhǔn)備的空間。Exchange函數(shù)不僅擁有傳進(jìn)來(lái)的形參變量a,b,還有自己定義的局部變量tmp。

第三步的push ecx,是為局部變量tmp準(zhǔn)備的,這個(gè)操作后,esp向地址低處移動(dòng)4B,相當(dāng)于偏移量為-4;

與此同時(shí),堆棧里的情況如下圖所示:

                                                         

實(shí)參變量是調(diào)用函數(shù)Exchange時(shí)入棧的,而且是根據(jù)括號(hào)里的順序($x,$y)從右往左依次入棧。

而賦值給形參變量是從左往右賦值的,也就是上面所說(shuō)的int *a=&x與int *b=&y。往上,我們還要push兩個(gè)名為ebp和eip的值入棧,ebp入棧是因?yàn)槲覀円4鎚ain原ebp的值,而為什么這里還有4B的eip值也入棧了呢?

這是因?yàn)?,eip值是用來(lái)存放“下一步"要執(zhí)行的某個(gè)進(jìn)程(線程)的代碼的地址的,主函數(shù)調(diào)用子函數(shù),主函數(shù)被”中斷“,當(dāng)子函數(shù)返回到主函數(shù)時(shí),同時(shí)也應(yīng)該準(zhǔn)確地返回到主函數(shù)被中斷的地方,繼續(xù)往下執(zhí)行。

所以eip是保護(hù)”現(xiàn)場(chǎng)信息“的關(guān)鍵。于是,這也就解釋了為什么形參變量a和b與ebp的偏移量分別為8B和12B了。

如果子函數(shù)在其內(nèi)部繼續(xù)定義局部變量,那么空間就繼續(xù)往上走,esp指針也跟著往上走就可以了。

函數(shù)調(diào)用的入棧順序就很明了了:實(shí)參表達(dá)式(形參)->main()eip->main()ebp->被調(diào)函數(shù)局部變量。如果子函數(shù)中再調(diào)用其它函數(shù),就如printf(),入棧細(xì)節(jié)也是如法炮制的。

但是,這好像依舊沒(méi)搞清楚最開(kāi)始的問(wèn)題,即它們是如何成功交換值的。要搞清楚這個(gè),我們就必須明白主函數(shù)局部變量,實(shí)參和形參三者的所占用空間關(guān)系。

其重點(diǎn)就是,局部變量與實(shí)參并不是占用同一空間,然而實(shí)參表達(dá)式算出來(lái)的值,在系統(tǒng)堆棧占用的空間,就是與之對(duì)應(yīng)的形參變量的空間。

也就是說(shuō),如果我們按開(kāi)頭第一種代碼寫交換功能的話,我們交換的只是,形參變量空間中的值而已,因?yàn)樗幌喈?dāng)于復(fù)制了一份主函數(shù)中x,y的值給形參而已,并沒(méi)有產(chǎn)生能夠改變真正在主函數(shù)中x,y的值。

但是如果我們傳遞給Exchagne的是x,y的地址的話,就相當(dāng)于構(gòu)建了Exchange中形參變量a和b與主函數(shù)x,y的一種地址指向”聯(lián)系“。

一旦我們建立了這種聯(lián)系,我們就可以將a所指向空間的值(局部變量x的值)與b所指向空間的值(局部變量y的值)同過(guò)tmp進(jìn)行交換。從而達(dá)成了交換的功能。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • C語(yǔ)言約瑟夫環(huán)的實(shí)現(xiàn)

    C語(yǔ)言約瑟夫環(huán)的實(shí)現(xiàn)

    這篇文章主要介紹了C語(yǔ)言約瑟夫環(huán)的實(shí)現(xiàn)的相關(guān)資料,這里主要是利用數(shù)據(jù)數(shù)據(jù)結(jié)果中循環(huán)鏈表來(lái)實(shí)現(xiàn),需要的朋友可以參考下
    2017-08-08
  • C++線程中幾類鎖的詳解

    C++線程中幾類鎖的詳解

    這篇文章主要為大家介紹了C++線程中幾類鎖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2021-11-11
  • c++快速排序詳解

    c++快速排序詳解

    快速排序總體思想:先找到一個(gè)樞軸,讓他作為分水嶺,通過(guò)一趟排序?qū)⒋判虻挠涗浄指畛蓛刹糠?前面一部分都比樞軸小,后面一部分樞軸大,然后又分別對(duì)這兩部分記錄繼續(xù)進(jìn)行遞歸的排序,達(dá)到整個(gè)序列有序的目的
    2017-05-05
  • C++中整形與浮點(diǎn)型如何在內(nèi)存中的存儲(chǔ)詳解

    C++中整形與浮點(diǎn)型如何在內(nèi)存中的存儲(chǔ)詳解

    大家好!這期和大家分享整形和浮點(diǎn)型是如何在數(shù)據(jù)是如何在內(nèi)存中存儲(chǔ),下面文章具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-05-05
  • C語(yǔ)言入門學(xué)習(xí)筆記之typedef簡(jiǎn)介

    C語(yǔ)言入門學(xué)習(xí)筆記之typedef簡(jiǎn)介

    typedef為C語(yǔ)言的關(guān)鍵字,作用是為一種數(shù)據(jù)類型定義一個(gè)新名字,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言入門學(xué)習(xí)筆記之typedef簡(jiǎn)介的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • C++?opencv利用grabCut算法實(shí)現(xiàn)摳圖示例

    C++?opencv利用grabCut算法實(shí)現(xiàn)摳圖示例

    這篇文章主要為大家介紹了C++?opencv利用grabCut算法實(shí)現(xiàn)摳圖的代碼示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • C語(yǔ)言實(shí)現(xiàn)兩個(gè)遞減數(shù)列中尋找某一個(gè)數(shù)

    C語(yǔ)言實(shí)現(xiàn)兩個(gè)遞減數(shù)列中尋找某一個(gè)數(shù)

    這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)兩個(gè)遞減數(shù)列中尋找某一個(gè)數(shù),是一類經(jīng)典的數(shù)組操作算法,需要的朋友可以參考下
    2014-09-09
  • linux c 獲得當(dāng)前進(jìn)程的進(jìn)程名和執(zhí)行路徑(示例)

    linux c 獲得當(dāng)前進(jìn)程的進(jìn)程名和執(zhí)行路徑(示例)

    如何得到當(dāng)前進(jìn)程的進(jìn)程名和執(zhí)行路徑。寫了個(gè)程序分享一下
    2013-07-07
  • opencv學(xué)習(xí)筆記C++繪制灰度直方圖

    opencv學(xué)習(xí)筆記C++繪制灰度直方圖

    這篇文章主要為大家介紹了opencv學(xué)習(xí)筆記C++繪制灰度直方圖的實(shí)現(xiàn)代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • C語(yǔ)言讀取和存儲(chǔ)bmp格式圖片

    C語(yǔ)言讀取和存儲(chǔ)bmp格式圖片

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言讀取和存儲(chǔ)bmp格式圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10

最新評(píng)論

隆回县| 昌乐县| 沙田区| 云安县| 迭部县| 灌南县| 鄂温| 集安市| 新安县| 临沭县| 娄烦县| 炎陵县| 凌海市| 永清县| 蒙城县| 肃北| 桐庐县| 泰兴市| 清水河县| 铜陵市| 汝阳县| 容城县| 通城县| 阿巴嘎旗| 蒙阴县| 黄大仙区| 和龙市| 惠来县| 陆良县| 林口县| 水富县| 西贡区| 常德市| 叶城县| 淄博市| 扶风县| 石河子市| 曲沃县| 尤溪县| 遂川县| 师宗县|