C++疑難:調(diào)用類構(gòu)造函數(shù)、成員函數(shù)致程序啟動時訪問空指針錯誤Access Memory at 0x0
????這個錯誤你可能沒有遇到過,也可能永遠不會遇到,但如果遇到了,需要巨大的代價去找到它,因為錯誤發(fā)生在main()函數(shù)之前,常規(guī)的調(diào)試手段無法找出錯誤緣由。我只知道出現(xiàn)這個錯誤的源頭在哪,卻無法解釋為何會出現(xiàn)這種現(xiàn)象,這應該涉及底層對象模型。造成該錯誤的實際原因幾乎都是因為全局變量或靜態(tài)成員變量初始化報錯(這些過程在main()之前)導致的,但你在調(diào)試時調(diào)試器根本不會追蹤到這些初始化失敗的變量,而是直接在主線程0x0退出,沒有給出任何有效信息。
????在Google和StackOverflow上暫未找到類似疑問及答復,因而在此記錄以作參考。
????具體講這個錯誤的表現(xiàn)如下:你寫了一個類A,里面可能有靜態(tài)函數(shù),可能有內(nèi)聯(lián)函數(shù),也可能有普通的成員函數(shù),并且你已經(jīng)檢查過了無數(shù)次,代碼沒有任何錯誤(實際上確實沒有錯誤,因為錯誤根源不出現(xiàn)在類A中,而是出在全局變量/靜態(tài)成員變量里,但你的調(diào)試器無法追蹤到它們),但是運行、調(diào)試時卻總是在main()之前就程序崩潰,且無法給出除訪問無效指針(空指針及其附近的地址)以外的任何有效信息。
????下面是一個簡單的例子。類B有一個全局變量b,但是這個全局變量b因為某些原因初始化失敗(在我的代碼中,是由于free()了一個錯誤的內(nèi)存地址)。類A中有內(nèi)聯(lián)函數(shù),有成員函數(shù),但其中只有memb_func2()對全局變量b進行了訪問及操作。
????需要注意的是,類A的構(gòu)造函數(shù)為inline,這意味著構(gòu)造函數(shù)不會被儲存在類的函數(shù)指針表里,而是直接內(nèi)聯(lián)到代碼中(嚴格講是否內(nèi)聯(lián)由編譯器決定,在這里我們假設(shè)這個構(gòu)造函數(shù)足夠簡單,編譯器一定會內(nèi)聯(lián)它)

當你在main()函數(shù)里寫下如下代碼

此時程序正常運行,因為構(gòu)造函數(shù)為inline(也可以是default),沒有涉及到非內(nèi)聯(lián)函數(shù)。但如果把構(gòu)造函數(shù)改為非inline

那么同樣上面的代碼,此時卻出現(xiàn)運行崩潰,且崩潰發(fā)生main()之前。我們現(xiàn)在知道崩潰是由于全局變量b未正確初始化(拋出了錯誤)導致的。但是,只有memb_func2()訪問了b,而main()中只調(diào)用了沒有訪問b的非內(nèi)聯(lián)構(gòu)造函數(shù),并沒有調(diào)用訪問了b的memb_func2(),卻依然導致了崩潰。如果打開調(diào)試器,那么你只能得到一個訪問了空指針的信息Access Memory at 0x0 或者 0x1、0x2 等從0x0開始算起的地址。
????回到內(nèi)聯(lián)構(gòu)造函數(shù)的情況,只調(diào)用內(nèi)聯(lián)函數(shù)inline_func()

程序依然是正確運行的,沒有錯誤。但是,一旦你調(diào)用memb_func1()或者memb_func2(),那么程序編譯雖然能通過,但同樣會出現(xiàn)和上面一模一樣的錯誤。


? ? 當然,你不一定非得調(diào)用,取成員函數(shù)的指針一樣會報錯,例如下面的代碼也會出現(xiàn)上面的錯誤


????因此,如果出現(xiàn)了這樣的錯誤,你應該檢查一下全局變量、靜態(tài)變量這些在main()之前執(zhí)行的操作是否拋出了錯誤,例如上面中類A的代碼沒有錯,錯誤在全局變量b上,但是調(diào)用A的非內(nèi)聯(lián)函數(shù)卻導致程序崩潰,給了一種錯誤出現(xiàn)在A上的錯覺,讓你一遍又一遍地檢查A的代碼卻始終無法解決問題。
????對這個錯誤最形象的說法就是一顆耗子屎打壞一鍋湯,就因為memb_func2()訪問了拋出錯誤的全局變量,導致A的所有非內(nèi)聯(lián)函數(shù)(包括構(gòu)造函數(shù)、析構(gòu)函數(shù)等)全都損壞,連取函數(shù)指針都會崩潰。當然實際上memb_func2()是無辜的,真正的耗子屎是全局變量b。