非常規(guī)入門之一:通用編程語言技術(shù)之上下文隱患(三)

在上文中,引入函數(shù)與接口的概念以將機器特性以及相關(guān)底層細節(jié)進行隱藏匆笤,其優(yōu)勢在于使用戶(程序員)不必時刻關(guān)心底層的硬件邏輯研侣,從而在一定程度上提高開發(fā)人員的開發(fā)效率。但是機器特性與相關(guān)底層細節(jié)的隱藏并不意味著開發(fā)人員能夠在抽象層面為所欲為炮捧,這反而要求必須關(guān)心機器配置和相關(guān)的代碼的效率問題庶诡。

故而在接下來的文章中,會為大家?guī)碓诔橄髮用孢M行功能開發(fā)需要注意的一些細節(jié)咆课。同時末誓,這一部分也是通用編程語言技術(shù)向上提供強大功能的基礎(chǔ)。請勿將此“基礎(chǔ)”與前兩章相提并論书蚪。前兩章內(nèi)容是通用編程語言技術(shù)得以實現(xiàn)的基礎(chǔ)喇澡。此處所指“向上”乃是面向應(yīng)用、邏輯或抽象層殊校。


一晴玖、由函數(shù)帶來的上下文問題

在前文中提到,函數(shù)的調(diào)用過程依次為如下9個步驟为流。在本節(jié)中呕屎,我們的重點放在發(fā)生跳轉(zhuǎn)的部分,即第1敬察、2秀睛、38莲祸、9步蹂安。

1椭迎、獲取上下文,即函數(shù)名
2田盈、跳轉(zhuǎn)至函數(shù)所在地址
3侠碧、保護上下文環(huán)境,即將之前的調(diào)用位置保存在棧中
4缠黍、為局部數(shù)據(jù)開辟空間
5弄兜、執(zhí)行函數(shù)體
6、將返回值保存在RAX寄存器中(可能有瓷式、可能沒有)
7替饿、釋放局部數(shù)據(jù)的空間(開辟多少就釋放多少)
8、預(yù)備恢復(fù)上下文環(huán)境贸典,將保存在棧中的前一個調(diào)用位置取出视卢。
9、恢復(fù)上下文環(huán)境廊驼,即跳轉(zhuǎn)至前一個調(diào)用位置

在之前的文章中据过,我們提到標識符是有效數(shù)據(jù)存儲在內(nèi)存中物理地址的字符化表示,同時我們也將函數(shù)視為一種特殊的數(shù)據(jù)妒挎。忘記的小伙伴可以通過第一章“基礎(chǔ)”的第五節(jié)中進行回顧绳锅。

函數(shù)名作為標識符進行識別,其在底層中也就是一個地址酝掩,只是在這個地址的內(nèi)存空間中存儲的是一段有意義的鳞芙、能夠執(zhí)行的邏輯代碼。同時期虾,函數(shù)又可以稱為“子程序”原朝、“過程”,因而在匯編語言中镶苞,函數(shù)的章節(jié)中喳坠,一般會使用“子程序設(shè)計”或“過程調(diào)用”等類似的說法,我們只需知道函數(shù)就是子程序茂蚓、就是過程壕鹉。

當然,可能有小伙伴會將函數(shù)和計算機組成原理中的中斷進行類比煌贴,雖然二者有一定的區(qū)別御板,但這種區(qū)別微乎其微锥忿,以至于基本沒有什么比較好的例子來舉證牛郑,所以也就可以把中斷當成函數(shù)來理解。

在上文列舉的步驟中敬鬓,我們可以得知淹朋,函數(shù)正式調(diào)用之前笙各,先獲取了函數(shù)所在內(nèi)存中的地址(即函數(shù)名)。此處會有若干個陷阱础芍,那就是:1杈抢、這個函數(shù)所在的地址不存在;2仑性、這個函數(shù)所在的地址上是空操作

下面進行分析:
1惶楼、函數(shù)所在地址不存在

在通用編程語言技術(shù)中,使用的語法都是高級語言語法诊杆,所以在編譯時中就會將這種由用戶(程序員)行為缺失導(dǎo)致的致命的語法錯誤識別出來歼捐。

如果編譯功能比較弱,不進行語法錯誤識別(當然晨汹,這是不太可能的)豹储,就需要由底層語言負責(zé)錯誤識別;如果底層語言仍然無法識別淘这,則有程序在運行時觸發(fā)程序錯誤中斷剥扣。

2、函數(shù)所在地址為空操作

若空操作由高級語言語法轉(zhuǎn)換而來铝穷,則必然不會出現(xiàn)純粹的空操作钠怯,在函數(shù)末尾會出現(xiàn)ret指令返回調(diào)用處;若純空操作(匯編語言nop指令)是由用戶(程序員)利用某些機制內(nèi)嵌匯編語言形成的(必然不可能)曙聂,則由底層語言進行優(yōu)化呻疹;若底層語言無法進行優(yōu)化(必然不會出現(xiàn)),則有程序在運行時出發(fā)程序運行時中斷(由于程序運行無操作出發(fā)筹陵,應(yīng)稱之為“警告”刽锤,因為其并未造成程序的崩潰)。

故而可以得出以下結(jié)論:
1朦佩、函數(shù)的調(diào)用行為包含三個動作:獲取函數(shù)地址并思,跳轉(zhuǎn),保存跳轉(zhuǎn)前地址语稠。
2宋彼、在第1點中,涉及到的兩個地址稱為上下文環(huán)境仙畦。
3输涕、函數(shù)調(diào)用的三個動作分別稱為調(diào)用前,調(diào)用時慨畸,調(diào)用后莱坎;“調(diào)用前”時刻的上下文就已經(jīng)確定下一個上下文;“調(diào)用時”時刻寸士,上下文發(fā)生變化檐什;“調(diào)用后”時刻碴卧,上下文將一直保持到函數(shù)返回后。

當函數(shù)的內(nèi)部邏輯處理完成后乃正,應(yīng)當還原到上一個上下文住册,但是,如果在函數(shù)調(diào)用后不進行保存瓮具,則整個程序的邏輯依舊會發(fā)生異常荧飞。所以,自高級語言誕生以來名党,函數(shù)調(diào)用的細節(jié)就始終交由編譯器進行處理垢箕,上下文環(huán)境也由編譯器增加的語句保存至棧中。故而伴隨函數(shù)調(diào)用的行為就是環(huán)境保護兑巾。注意:保存的是前一個上下文条获,而不是調(diào)用后的當前上下文,因為當前上下文就在寄存器中蒋歌,無需保存帅掘。

全局狀態(tài)下理論上不需要環(huán)境保護,但如今計算機系統(tǒng)中堂油,運行時(即進程和線程)不只一個修档,所以全局環(huán)境也會由編譯器增加若干語句,以完成全局環(huán)境的保護府框。

函數(shù)完成后的返回行為又可稱為上下文環(huán)境的恢復(fù)吱窝。包含兩個動作:獲取保存的上下文、返回迫靖≡合浚可能這里“返回”二字會使人滿臉問號。事實上系宜,在匯編語言中函數(shù)執(zhí)行后的返回行為只包含pop和ret兩個指令照激。pop用于將保存的上下文重新寫入RBP寄存器,RET指令用于跳轉(zhuǎn)至前一個上下文環(huán)境盹牧。

二俩垃、上下文環(huán)境的延伸:命名空間

命名空間是更高級、更復(fù)雜汰寓、動態(tài)的上下文環(huán)境口柳,在命名空間中能夠存儲更為復(fù)雜的數(shù)據(jù)。

在前一節(jié)中有滑,我們使用上下文環(huán)境可以得知函數(shù)之間的調(diào)用關(guān)系跃闹,很明顯,這只能用于面向過程。如果我們需要向面向?qū)ο筮^渡辣卒,就需要知道當前函數(shù)所屬于哪一個對象掷贾。

在上下文環(huán)境中睛榄,我們可以直接訪問RBP寄存器和棧就可以得知上下文環(huán)境荣茫,那么命名空間呢?
很顯然场靴,我們無法直接獲取命名空間啡莉,故而需要將其專門保存下來。實際上旨剥,如果你沒有學(xué)過C++或者沒有處理類似的問題咧欣,你可能永遠也碰不到命名空間的概念。

命名空間是用來組織和重用代碼的重要手段轨帜。這是一句廢話魄咕。然而就是這句廢話,卻能夠讓“通用編程語言技術(shù)”實現(xiàn)從面向過程到面向?qū)ο蟮馁|(zhì)性飛躍蚌父。

我們知道哮兰,一個類的不同對象的屬性值可能不一樣,但是對象使用的方法的內(nèi)部邏輯都是一致的苟弛,那么這些方法存儲在哪里呢喝滞?當然是全局區(qū)域。因為類的方法膏秫,不屬于任何一個對象右遭。但是類的方法可能不止一個,如果允許方法同名缤削,則參數(shù)有可能不一樣窘哈。如此該怎樣確定是哪一個對象在使用哪一個方法呢?

首先亭敢,我們應(yīng)該確定三條基本原則:
1宵距、命名空間的名稱應(yīng)當符合標識符的正規(guī)式(或正則表達式,都是一個東西)
2吨拗、命名空間的名稱在所在作用域中具有唯一性满哪。
3、命名空間允許嵌套一個或多個命名空間劝篷。

根據(jù)以上三條原則哨鸭,我們可以立即找到一個命名空間,即某一個程序的全局命名空間(namespace Global)娇妓,當然進程之間是不會重名的像鸡,程序的運行由操作系統(tǒng)控制,程序一旦成功申請內(nèi)存空間,操作系統(tǒng)就會為它分配一個有效的唯一的進程號只估,該進程號既可以用于操作系統(tǒng)管理進程的銷毀志群、內(nèi)存釋放,同時還可以作為這個進程內(nèi)部最頂層的命名空間蛔钙。進程與進程之間是相對獨立的锌云。進程間通信則需要依賴操作系統(tǒng)提供的服務(wù)。

既然命名空間名稱是一個標識符吁脱,也就意味著命名空間實際在機器內(nèi)部并不存在桑涎,即命名空間也是一個有效的地址。

而至于命名空間在物理內(nèi)存中存儲格式兼贡,實際是需要根據(jù)底層設(shè)計進行實現(xiàn)的攻冷,這也就意味著不同平臺可能實現(xiàn)的方式不一樣,不同編程語言的實現(xiàn)方式也不盡相同遍希。

至此等曼,不知道有沒有小伙伴發(fā)現(xiàn)一個問題:類和命名空間的作用如此類似!既然如此類似凿蒜,那么類是不是特殊的命名空間呢禁谦?作者個人認為,類應(yīng)該算是一種特殊的命名空間篙程,但是類和命名空間仍然存在區(qū)別枷畏。

命名空間的作用看起來比類的作用要少很多。命名空間的作用就是用來組織和重用代碼虱饿,但是類的功能遠不止于此拥诡。其實也沒有必要在命名空間和類之間糾結(jié)。

三氮发、上下文的夢魘:上下文對象

隨著更多的編程語言支持Lambda表達式(點名C++渴肉,其他高級語言早就支持了,C++11才支持)爽冕,上下文對象的身影越來越多仇祭,即使只是用JavaScript,上下文對象也能迷惑一陣子颈畸。

Lambda表達式乌奇,一些文檔成也稱之為匿名函數(shù)(如此說是因為如此稱呼并不一定符合編程語言特性,比如Java中就不完全正確)眯娱。

Lambda表達式又稱為閉包礁苗,其形式大致符合“參數(shù)列表=>內(nèi)部邏輯”。但是由于不同語言的函數(shù)定義方式不同徙缴,這也導(dǎo)致不同語言的閉包格式也是千奇百怪试伙。

/* C# */
(<參數(shù)列表>)=>{<語句>} // 只有一條語句可以省略大括號
/* Java */
(<參數(shù)列表>)->{<語句>} // 只有一個參數(shù)可以省略小括號
/* C++ */
[<傳值類型>](<參數(shù)列表>){<語句>}
/* JavaScript */
function (<參數(shù)列表>) {<語句>}
/* Python */
lambda <形參列表> : <函數(shù)返回值表達式語句>

雖然以上代碼看似簡單,但是“萬惡”的JavaScript又提供了一個讓人歡喜讓人狂的功能,如下:
1疏叨、JavaScript中閉包可以賦值給變量潘靖,此時變量稱為函數(shù)對象
2、JavaScript能夠通過函數(shù)形式構(gòu)造對象
3蚤蔓、函數(shù)自調(diào)用卦溢,形如

(function(<形式參數(shù)列表>){<語句>})(實參列表)

4、箭頭函數(shù)不是函數(shù)

前3條都好理解昌粤,唯有最后一條既绕。作者的理解是JavaScript引擎將箭頭函數(shù)直接解析為表達式啄刹,函數(shù)體內(nèi)部的上下文對象仍與外部一致涮坐。與一般的閉包形成對比,JavaScript中閉包的函數(shù)體內(nèi)部的上下文對象與外部不一致誓军,即JavaScript引擎將閉包解析成Window對象的一個方法袱讹,當條件觸發(fā)時,由Window對象來調(diào)用這個函數(shù)昵时,這也是前3條存在的因素捷雕。

或許大家能夠猜測到,本節(jié)所言上下文對象壹甥,實際指的就是this這個對象救巷。想要理解this的作用,只需要理解其底層的基礎(chǔ)句柠,也就是前2節(jié)的內(nèi)容浦译。


至此,面向?qū)ο蟮牡讓舆壿嬀徒Y(jié)束了溯职。通用編程語言技術(shù)也由面向過程編程過渡到了面向?qū)ο缶幊獭?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末精盅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谜酒,更是在濱河造成了極大的恐慌叹俏,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件僻族,死亡現(xiàn)場離奇詭異粘驰,居然都是意外死亡,警方通過查閱死者的電腦和手機述么,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門蝌数,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碉输,你說我怎么就攤上這事籽前。” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵枝哄,是天一觀的道長肄梨。 經(jīng)常有香客問我,道長挠锥,這世上最難降的妖魔是什么众羡? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮蓖租,結(jié)果婚禮上粱侣,老公的妹妹穿的比我還像新娘。我一直安慰自己蓖宦,他們只是感情好齐婴,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稠茂,像睡著了一般柠偶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上睬关,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天诱担,我揣著相機與錄音,去河邊找鬼电爹。 笑死蔫仙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的丐箩。 我是一名探鬼主播摇邦,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼雏蛮!你這毒婦竟也來了涎嚼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤挑秉,失蹤者是張志新(化名)和其女友劉穎法梯,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體犀概,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡立哑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了姻灶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铛绰。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖产喉,靈堂內(nèi)的尸體忽然破棺而出捂掰,到底是詐尸還是另有隱情敢会,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布这嚣,位于F島的核電站鸥昏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏姐帚。R本人自食惡果不足惜吏垮,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望罐旗。 院中可真熱鬧膳汪,春花似錦、人聲如沸九秀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽颤霎。三九已至媳谁,卻和暖如春涂滴,著一層夾襖步出監(jiān)牢的瞬間友酱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工缔杉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搁料。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓或详,卻偏偏與公主長得像,于是被迫代替她去往敵國和親郭计。 傳聞我的和親對象是個殘疾皇子霸琴,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容