<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • 一個C++引用庫的頭文件預編譯陷阱

    寫在前面

    老胡最近在工作中,有個場景需要使用一個第三方庫,引用頭文件,鏈接庫,編譯運行,一切都很正常,但是接下來就遇到了一個很詭異的問題,調用該庫的中的一個對象方法為對象修改屬性的時候,會影響到對象的另外一個屬性,當時百思不得其解,直呼靈異事件。
    但后面靜下心來細細看了一下代碼和各種配置,發現了問題所在,現在把這個問題分享在這里,希望大家在以后的工作中如果遇到了類似的情況知道應該如何處理。
     

    場景還原

    當時引用的是一個第三方的靜態鏈接庫,場景非常簡單,在項目中包含頭文件,鏈接器指定路徑和靜態庫名稱,我們這里新建工程來生成一個非常簡單的庫。

    其中,

    //LibObject.h
    #pragma once
    struct LibObject
    {
    	int valueA{ 0 };
    #ifdef AdditionalValue
    	int valueB{ 0 };
    #endif
    	int valueC{ 0 };
    
    	void DoSomething();
    };
    
    //LibObject.cpp
    #include "LibObject.h"
    
    void LibObject::DoSomething()
    {
    	valueA = 10;
    #ifdef AdditionalValue
    	valueB = 10;
    #endif
    }
    

    簡單至極,若預編譯變量定義了AdditionalValue則定義多一個valueB并且在方法中賦值。編譯庫的時候我們指定AdditionalValue
     

    客戶端代碼

    //main.cpp
    
    #include "LibObject.h"
    #include <iostream>
    using namespace std;
    int main()
    {
    	LibObject obj;
    	cout << obj.valueA << endl;
    	cout << obj.valueC << endl;
    	obj.DoSomething();
    	cout << obj.valueA << endl;
    	cout << obj.valueC << endl;
    	return 0;
    }
    

    客戶端代碼也很簡單,聲明一個對象,調用它的方法并在調用前后檢查它的值,在編譯客戶端代碼的時候,我們不定義AdditionalValue預編譯變量。
     

    運行試試

    現在猜一猜輸出是多少?

     

    解惑

    藏在背后的秘密

    如果這個結果讓你吃驚,那么相信我,你不是一個人,當時老胡也驚呆了,不管怎么看,DoSomething僅僅修改了ValueA,為什么會讓ValueC的值變了?

     
    秘密就在于編譯庫的時候和編譯客戶端代碼的時候,我們使用了不同的預編譯變量。

    • 在客戶端代碼看來,LibObject是一個僅僅包含2個int類型的結構體,并且DoSomething方法會賦值給一個int,該int相對于this指針偏移是0。
    • 另一方面,在庫代碼看來,這個結構體包含了3個int類型變量,DoSomething會賦值給相對于this指針偏移為0和4的兩個int。

    所以答案揭曉了,為什么valueC的值會被影響,在于DoSomething執行的時候,相當于this指針偏移為4的int被賦值了,但是在我們從客戶端代碼構建的結構體中,這個位置存放的是valueC。

    從這里可以看出,在方法執行的過程中,所謂的valueB其實內存地址和valueC是一樣的。所以其實是那句給valueB賦值的語句把值給了valueC。
     

    如何修復

    知道了出問題的地方,修復起來就很簡單了,一般來說兩個辦法。

    • 如果第三方庫能找到源代碼,那我們可以重新用我們希望的預編譯設置編譯一次
    • 如果找不到源代碼,那我們只有在客戶端代碼添加相應的預編譯設置,確保和編譯庫時候所使用的一致

    這兩個辦法都需要仔細閱讀第三方庫的文檔。
     
    希望本文能給遇到了類似問題的小伙伴一點啟示,特別當你遇到了類似的情況的時候,這篇文章能夠給你一些思路,畢竟,編譯器甚至在這種情況下都不會給出任何警告,我們只能靠經驗排查了。

    posted on 2021-03-25 20:09  老胡寫代碼  閱讀(286)  評論(0編輯  收藏  舉報

    導航

    国产美女a做受大片观看