【異常案例分析】使用空指針調用函數(非虛函數)時,沒有崩潰在函數調用處,而是崩在被調用函數內部

目錄

1、問題說明

2、代碼段地址與數據段地址

3、使用空指針調用BindWindow函數(非虛函數),沒有崩在BindWindow函數的調用處,而是崩在函數內部

3.1、虛函數調用的二次尋址

3.2、崩潰在被調用函數內部

4、總結


C++軟件異常排查從入門到精通系列教程(核心精品專欄,訂閱量已達8000多個,歡迎訂閱,持續更新...)https://blog.csdn.net/chenlycly/article/details/125529931C/C++實戰專欄(重點專欄,專欄文章已更新500多篇,訂閱量已達6000多個,歡迎訂閱,持續更新中...)https://blog.csdn.net/chenlycly/article/details/140824370C++ 軟件開發從入門到實戰(重點專欄,專欄文章已更新300多篇,歡迎訂閱,持續更新中...)https://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能開發匯總(專欄文章列表,歡迎訂閱,持續更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++軟件分析工具從入門到精通案例集錦(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/article/details/131405795開源組件及數據庫技術(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/category_12458859.html網絡編程與網絡問題分享(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/category_2276111.html? ? ? ?同事那邊最近遇到了一個空指針引發的崩潰,使用空指針調用一個成員函數,但并沒有崩潰在函數調用處,而是崩潰在被調用函數內部。同事搞不懂這個細節,找到我,讓我幫忙看看,給詳細講解一下具體的原因。今天我就把相關的細節詳細說明一下,以供大家參考。

1、問題說明

? ? ? ?最近同事遇到一個關于程序崩潰點的問題,找到我,想問一下是原因。發生崩潰的代碼上下文如下所示:

代碼中使用iter.second.pVideDec指針調用了BindWindow函數。同事可以確定的是,iter.second.pVideDec指針的值為NULL導致的崩潰,但令同事感到疑惑的是,為什么沒有崩潰在使用空指針調用BindWindow函數的函數調用處,而是崩潰在BindWindow函數內部(代碼運行到BindWindow函數內部才觸發了崩潰)?更進一步,進入BindWindow函數內部后,沒有去call函數CallMedia就發生了崩潰,這又是什么原因呢?

? ? ? ?這兩個問題其實很簡單,但實際上大部分C++開發人員并不懂,也講不清楚!

2、代碼段地址與數據段地址

? ? ? ?我們在看問題之前,我們需要弄清楚代碼段地址與數據段地址的區別。C++程序在運行時所占用的內存區域,一般可分為棧內存區、堆內存區、全局/靜態內存區、文字常量內存區及程序代碼區5大分區,如下所示:

程序中變量占用的內存屬于數據段內存區,包含棧內存區、堆內存區、全局/靜態內存區、文字常量內存區。

? ? ? ?程序啟動時會將依賴的各個二進制模塊以及程序主模塊加載到進程空間中,這些二進制模塊要占用內存空間,這些內存空間是從進程的內存空間劃撥的。具體的是,二進制模塊中的二進制代碼占用內存空間,這些內存空間中存放的都是二進制代碼,所以叫代碼段內存區。C++代碼中調用函數,從匯編代碼的角度去看,就是去call函數的地址(代碼段地址)。程序一旦啟動起來,加載起來的所有模塊中的函數地址就確定下來了。

? ? ? ?關于C++程序的五大內存分區的詳細說明,可以查看我的文章:

實例詳解C++程序的五大內存分區https://blog.csdn.net/chenlycly/article/details/120958761


? ? ? ?在這里,給大家重點推薦一下我的幾個熱門暢銷專欄,歡迎訂閱:(博客主頁還有其他專欄,可以去查看)

專欄1:該精品技術專欄的訂閱量已達到10000多個,專欄中包含大量項目實戰分析案例,有很強的實戰參考價值,廣受好評!專欄文章持續更新中,已經更新到210篇以上!歡迎訂閱!)

C++軟件調試與異常排查從入門到精通系列文章匯總https://blog.csdn.net/chenlycly/article/details/125529931

本專欄根據多年C++軟件異常排查的項目實踐,系統地總結了引發C++軟件異常的常見原因以及排查C++軟件異常的常用思路與方法詳細講述了C++軟件的調試方法與手段詳細介紹分析C++軟件問題的常用分析工具,以圖文并茂的方式給出具體的項目問題實戰分析實例(詳細講述分析排查過程,很有實戰參考價值),帶領大家逐步掌握C++軟件調試與異常排查的相關技術,適合基礎進階和想做技術提升的相關C++開發人員!

考察一個開發人員的水平,一是看其編碼及設計能力,二是要看其軟件調試能力!所以軟件調試能力(排查軟件異常的能力)很重要,必須重視起來!能解決一般人解決不了的問題,既能提升個人能力及價值,也能體現對團隊及公司的貢獻!

專欄中的文章都是通過項目實戰總結出來的,包含大量項目問題實戰分析案例,有很強的實戰參考價值!專欄文章還在持續更新中,預計文章篇數能更新到300篇以上!

專欄2:(本專欄涵蓋了C++多方面的內容,是當前重點打造的專欄,訂閱量已達8000多個,專欄文章已經更新到500多篇,持續更新中...)

C/C++實戰進階(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/category_11931267.html

以多年的開發實戰為基礎,總結并講解一些的C/C++基礎與項目實戰進階內容,以圖文并茂的方式對相關知識點進行詳細地展開與闡述!專欄涉及了C/C++領域多個方面的內容,包括C++基礎及編程要點(模版泛型編程、STL容器及算法函數的使用等)、數據結構與算法C++11及以上新特性(開源代碼中可能會用到很多新特性(比如WebRTC開源庫),日常編碼中也會用到部分新特性,面試時也會頻繁地涉及到,學習新特性很有必要)、常用C++開源庫的介紹與使用(比如SQLite、libcurl、libwebsockets、libevent、jsoncpp/RapidJson、Redis、RabbitMQ、MongoDB、MQTT、ZooKeeper、OpenCV、FFmpeg、SDL、GStreamer、Live555、ReactOS等)、代碼分享(調用系統API、使用開源庫)、常用編程技術(動態庫、多線程、多進程、數據庫及網絡編程等)、軟件UI編程(Win32/duilib/QT/MFC)、C++軟件調試技術(引發C++軟件異常的常見原因分析與總結、排查C++軟件異常的手段與方法、分析C++軟件異常的基礎知識、使用常用軟件分析工具分析C++軟件問題、多個項目實戰問題分析案例分享等)、設計模式(單例模式、工廠模式、觀察者模式、狀態模式等)、網絡基礎知識與網絡問題分析進階內容(實戰問題分析實例分享)等。本專欄的內容都是建立在項目實踐的基礎上,來源于項目實戰,服務于項目實戰,很有實戰參考價值!

專欄3:??

C++常用軟件分析工具從入門到精通案例集錦匯總(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/article/details/131405795

常用的C++軟件輔助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本專欄詳細介紹如何使用這些工具去巧妙地分析和解決日常工作中遇到的問題,很有實戰參考價值!

專欄4:???

VC++常用功能開發匯總(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/article/details/124272585

將10多年C++開發實踐中常用的功能,以高質量的代碼展現出來。這些常用的高質量規范代碼,可以直接拿到項目中使用,能有效地解決軟件開發過程中遇到的問題。

專欄5:?

C++ 軟件開發從入門到精通(專欄文章,持續更新中...)https://blog.csdn.net/chenlycly/category_12695902.html

根據多年C++軟件開發實踐,詳細地總結了C/C++軟件開發相關技術實現細節,分享了大量的實戰案例,很有實戰參考價值。


3、使用空指針調用BindWindow函數(非虛函數),沒有崩在BindWindow函數的調用處,而是崩在函數內部

? ? ? ?使用空指針去調用BindWindow函數,是去call函數代碼段地址,這個操作不會導致崩潰,因為這個過程中沒訪問不該訪問的內存。BindWindow函數在程序啟動起來后就確定了,去call這個函數地址沒有問題,不會發生異常。但如果這個BindWindow是虛函數,則會崩潰在函數調用處。

3.1、虛函數調用的二次尋址

? ? ? ?如果這個BindWindow是虛函數,調用虛函數時要找到該虛函數的地址然后去call這個地址完成虛函數調用,在獲取虛函數地址的過程中會涉及到二次尋址:(此處講的尋址,是從匯編的角度去看的,從匯編代碼的角度可以清晰地看到二次尋址的完整過程

1)讀出類對象中虛函數表指針中的值,就是虛函數表的首地址,這是第一次尋址。讀取虛函數表指針變量的值,就是讀取虛函數表指針變量所在內存中的內容,首先要拿到虛函數表指針變量的內存地址,有了地址,才能讀取內存中的內容。對于C++類(對象),從類中成員變量的內存排布上看,作為成員變量的虛函數表指針變量是排在首位的,所以虛函數表指針變量的首地址,就是當前C++類對象的首地址。如果當前C++對象地址為NULL,就會把NULL作為虛函數表指針的首地址去讀取虛函數表指針的值,就會訪問地址為0的內存,這個小地址是禁止訪問的,所以會觸發內存訪問違例引發崩潰。
2)第一步中拿到虛函數表的首地址,找到虛函數表的位置,然后根據目標虛函數在當前類對象中的排位,得到地址偏移,然后到虛函數表中找到目標虛函數的地址,然后去call這個地址就完成虛函數調用了。這是第二次尋址。

? ? ? ?關于虛函數調用的二次尋址的詳細說明,可以查看我的文章:(從匯編的角度去看)
幾秒讀懂C++虛函數調用的匯編代碼實現https://blog.csdn.net/chenlycly/article/details/121046234

3.2、崩潰在被調用函數內部

? ? ? ?上面講了,程序沒有崩潰在BindWindow函數的調用處,這樣代碼就運行到這個BindWindow函數內部了。

BindWindow函數內部并沒有做什么復雜的事情,就是去調用另一個函數CallMedia。程序的崩潰發生在調用CallMedia的這行代碼上,為什么呢?因為在調用CallMedia函數時訪問當前類的成員變量m_dwChanId,把該成員變量作為參數傳遞給CallMedia,這就意味著要訪問到成員變量m_dwChanId,那具體是怎么訪問到m_dwChanId成員變量的呢?還是要從匯編代碼的角度去看,因為成員變量m_dwChanId是調用CallMedia的一個參數,所以在call這個CallMedia函數之前需要將成員變量m_dwChanId的值從內存中讀出來,然后將該值push壓到棧上,通過棧傳遞給被調用函數CallMedia。而當前傳入的C++類對象的地址為NULL,而做成類成員變量的m_dwChanId的地址是相對所在類對象地址的偏移,所以此時m_dwChanId變量的首地址是個很小的地址,而很小的地址是禁止訪問的,從而引發內存訪問違例,導致程序崩潰。

? ? ? ?上面我們多次講到小地址內存區,關于小地址內存區的詳細說明,可以查看我的文章:在文章的第8節有詳細的說明

引發C++程序內存錯誤的常見原因分析與總結https://blog.csdn.net/chenlycly/article/details/139596707如果BindWindow函數中沒有訪問所在類的成員變量的值,只是訪問函數中的局部變量,則不會導致內存訪問違例,不會引發程序崩潰。

? ? ? ?上面我們多次講到了從匯編代碼的角度去看問題分析問題,對于C++程序員,是很有必要了解并學習匯編。匯編代碼不僅能幫我們理解很多高級語言不好理解的代碼執行細節,而且可以輔助分析排查C++軟件異常問題。關于為什么要學習匯編代碼以及如何學習匯編,可以查看我的文章:

C/C++程序員為什么要了解匯編?了解匯編有哪些好處?如何學習匯編?https://blog.csdn.net/chenlycly/article/details/142795872

4、總結

? ? ? ?本例中遇到的問題場景,大部分C++開發人員搞不清楚引發崩潰的根本原因,只是知道空指針或者野指針可能會引發程序崩潰(訪問空指針或野指針可不是一定會引發程序崩潰的,要從內存的角度的分析)。但作為有經驗的開發人員,是需要搞清楚引發崩潰的根本原因的,必要時要結合匯編代碼去理解去分析。

有些問題看上去似乎很簡單,甚至理所當然,但是進一步深究的話,可能可以挖掘出一些細節的!遇到問題要多思考!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917499.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917499.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917499.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

鎖定中科院1區TOP!融合LSTM與Attention做時間序列預測 !

Transformer雖火,但在數據少、要求穩的時序預測場景中,LSTM仍是首選。尤其加上注意力機制后,更是彌補了LSTM的短板,增強了性能,實現了更精確的預測。這種組合不僅應用場景廣泛,工業界愛,學術界也…

在不可更改系統上構建數據響應機制的可選策略

在現代企業信息系統架構中,我們常常面臨如下挑戰:某個業務系統屬于“不可變更系統”,我們既不能修改其業務邏輯,也不能對其核心代碼做任何侵入式改動。但與此同時,我們又需要對該系統中的某些關鍵業務數據變更做出響應…

Docker 實戰 -- cloudbeaver

文章目錄前言文件目錄docker-compose.yml網絡連接前言 當你迷茫的時候,請點擊 Docker 實戰目錄 快速查看前面的技術文章,相信你總能找到前行的方向 上一篇文章 Docker 實戰 – Mysql 講述了用 docker 搭建 mysql 數據庫的過程, 連接數據庫的工具很多, …

Rust × WebAssembly 項目腳手架詳解

一、模板概覽 模板生成方式核心用途典型角色wasm-pack-templatecargo generate …把 Rust 代碼 打包成 npm 庫「底層算法/組件」作者create-wasm-appnpm init wasm-app構建純 JS/TS 項目,消費上面生成的 npm 包Web 前端/Node 服務rust-webpack-templatenpm init ru…

RSA 解密邏輯

以下是使用類的方式封裝 RSA 解密邏輯,使其更易于調用和管理: from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 import base64 class RSADecryptor:"""RSA 解密工具類,封裝解密邏輯,方便…

Oracle 19C 在centos中安裝操作步驟和說明

1、oracle 一到五,是在centos下,搭建數據的每個具體詳細步驟。 oracle:一、環境準備-CSDN博客 oracle:二、centos下安裝oracle-CSDN博客 oracle :三、配置LISTEN-CSDN博客 oracle:四、創建數據庫-CSDN博客 oracle:五、配置修改-CSDN博客…

《前端無障礙設計的深層邏輯與實踐路徑》

一個Web應用的價值不僅在于其功能的豐富性,更在于它能否向所有用戶敞開大門。那些被忽略的交互細節—一段沒有替代文本的圖片、一個無法通過鍵盤觸發的按鈕、一組對比度不足的文字——正在悄然構建起一道無形的壁壘,將部分用戶隔絕在數字世界之外。前端無障礙設計(A11y)的本…

ctfshow-web入門-254-266-反序列化

web254 代碼審計&#xff0c;輸入給的username和password ?usernamexxxxxx&passwordxxxxxx web255 這題要從cookie中獲取值并且需要把isVip設為true&#xff0c;并且將序列化之后的結果進行url編碼 <?php class ctfShowUser{public $usernamexxxxxx;public $passw…

ssh服務器端口和本地端口映射

由于服務器防火墻設置&#xff0c;本地能ssh登錄遠程服務器&#xff0c;但本地不能通過http的方式訪問服務&#xff0c;如tensorborad、gradle或其他服務。在不需要修改防火墻安全設置的情況下&#xff0c;這里我們臨時通過ssh端口映射的方式&#xff0c;在本地瀏覽器訪問這些服…

計算機網絡——UDP

1. UDP的背景 1&#xff09;先有TCP&#xff0c;后覺笨重 在TCP被首次提出后&#xff0c;將“可靠傳輸&#xff0c;流量控制&#xff0c;擁塞控制”全做在一個協議里隨著應用增多 ——> 很多場景&#xff08;語音&#xff0c;視頻&#xff09;并不需要萬無一失 ——> 更…

常見的深度學習模塊/操作中的維度約定(系統性總結)

&#x1f7e9; 1. 數據張量&#xff08;特征圖&#xff09;維度這是我們喂進網絡或從網絡中出來的“實際數據”。類型維度格式舉例說明圖像/特征圖(B, C, H, W)(4, 3, 32, 32)PyTorch中最常用的數據布局&#xff08;NCHW&#xff09;圖像/特征圖&#xff08;TensorFlow風格&…

【筆記】重學單片機(51)(上)

為學習嵌入式做準備&#xff0c;重新拿起51單片機學習。此貼為學習筆記&#xff0c;僅記錄易忘點&#xff0c;實用理論基礎&#xff0c;并不是0基礎。 資料參考&#xff1a;清翔零基礎教你學51單片機 51單片機學習筆記1. C語言中的易忘點1.1 數據類型1.2 位運算符1.3 常用控制語…

Arrays.asList() add方法報錯java.lang.UnsupportedOperationException

1. 問題說明 記錄一下遇到的這個bug&#xff0c;下面是段個簡化后的問題重現代碼。 public class Test {public static void main(String[] args) {List<Integer> list Arrays.asList(1, 2, 3);list.add(4);} }2. 原因分析 我們看一下Arrays.asList(…) 的源碼&#xff…

克羅均線策略思路

一個基于移動平均線的交易策略&#xff0c;主要通過比較不同周期的移動平均線來生成買賣信號。該策略交易邏輯思路和特點&#xff1a;交易邏輯思路1. 多頭交易邏輯&#xff1a;- 當當前周期的收盤價高于其4周期移動平均線&#xff0c;并且4周期移動平均線高于9周期移動平均線&a…

Go語言--語法基礎7--函數定義與調用--自定義函數

函數是基本的代碼塊&#xff0c;用于執行一個任務。Go 語言最少有 1 個 main() 函數。你可以通過函數來劃分不同功能&#xff0c;邏輯上每個函數執行的是指定的任務。函數聲明告訴了編譯器函數的名稱、返回類型和參數。函數三要素名稱 》功能參數 》接口返回值 》結果函數分類內…

Ollama模型庫模型下載慢完美解決(全平臺)

前言在我們從ollama下載模型時,會發現ollama最開始下載速度很快,能達到10-20MB/s但到了后期,速度就會越來越慢,最終降低到10-20kb/s下載一個模型大多需要1到1.5小時這是因為ollama服務器負荷過大的問題思路如果在下載中終斷下載,在用ollama run恢復下載,速度就會又提上去,但3-4…

web:js的模塊導出/導入

一般web頁面中&#xff0c;html文件通過標簽script引用js文件。但是js文件之間的引用要通過import/exprot進行導入/導出&#xff0c;同時還要在html文件中對js文件的引用使用type屬性標注。在下面的例子中&#xff0c;html頁面<!DOCTYPE html> <html lang"en&quo…

關于Web前端安全防御之安全頭配置

一、核心安全頭的作用 1. X-Content-Type-Options: nosniff 該響應頭用于阻止瀏覽器對資源的 MIME 類型進行 “嗅探”&#xff08;猜測&#xff09;&#xff0c;強制瀏覽器嚴格遵守服務器返回的 Content-Type 頭部聲明。 風險背景&#xff1a; 瀏覽器默認會對未明確聲明類型…

C++ : 反向迭代器的模擬實現

一、reverse_iterator.h#pragma once namespace txf { //外界傳什么類型的iteator&#xff0c;它就用什么iterator 初始化 , list用_list_iterator<T,T&,,T*> ,vector<T> 用T*template<class Iterator,class Ref,class Ptr>//在這個反向迭代器中涉及到…

自動化與配置管理工具 ——SaltStack

一、SaltStack 概述1.1 核心特性SaltStack 是一款開源的自動化運維工具&#xff0c;采用客戶端 - 服務器&#xff08;C/S&#xff09;架構&#xff0c;以高效、靈活和可擴展著稱。其核心特性包括&#xff1a;高性能架構&#xff1a;基于 ZeroMQ 消息隊列&#xff0c;支持大規模…