2013-11-25 71 views
15

對於該簡化的測試案例:「4字節」警告意味着什麼「填充類測試儀」?

#include <map> 

class Tester { 
    int foo; 
    std::map<int, int> smap; 
}; 

int main() { 
    Tester test; 
    return 0; 
} 

我得到以下編譯器警告:

$ clang++ -std=c++98 -Weverything test.cc 
test.cc:5:24: warning: padding class 'Tester' with 4 bytes to align 'smap' [-Wpadded] 
    std::map<int, int> smap; 
        ^

誰能解釋一下這個警告手段,我應該怎麼解決這個問題?

+14

這意味着你已經打開了太多的警告。 :) –

+0

是的,struct padding是一個*特性*,不是要警告的事情。我想國旗在那裏,因此需要知道內存中結構的*精確*佈局的人可以添加明確的填充,並在每次錯過時提醒他們,但這根本不是正常情況。 – hobbs

+1

@RetiredNinja聖牛人,完全代表你的屏幕名稱:) – bobobobo

回答

10

這裏沒有真正的問題。在C和C++中,編譯器允許在結構成員之後插入填充以提供更好的對齊,從而允許更快的內存訪問。在這種情況下,它看起來已經決定將smap放在一個8字節的對齊上。由於int幾乎可以肯定是四個字節,警告告訴你在結構中間有四個字節的空間浪費。

如果有更多的結構成員,那麼你可以嘗試的一件事是切換定義的順序。例如,如果您有Tester成員:

struct Tester { 
    int foo; 
    std::map<int, int> smap; 
    int bar; 
}; 

那就有意義兩個整數旁邊放置彼此進行優化調整,避免浪費的空間。但是,在這種情況下,只有兩個成員,如果切換它們,那麼編譯器可能會在結構的最後添加四個填充字節,以便優化Tester放置在數組中時的對齊方式。

+0

謝謝!在'smap'之後放置'foo'將發出不同的警告:''Tester'的填充大小爲4個字節到對齊邊界',可能意味着那些4個填充字節被插入到類的結尾之前,而不是' smap'。我也可以通過將'foo'轉換爲'long'來消除警告。唯一的問題是:當'int foo'是'Tester'中唯一的變量時,爲什麼我不會收到警告? –

+0

@DunPeal因爲整數只需要4字節的對齊方式,所以它不需要在結構的末尾添加任何填充以便將它們最優地放置在數組中。如果您嘗試將'foo'改爲'short'或'bool',您可能會看到警告再次彈出(再次,您可能不會,我不確定)。 –

+0

從這個問題的答案中,我認爲我的平臺(LP64)上的編譯器總是努力確保每個結構都分配了64的倍數?否則,爲什麼在'foo'是最後一個成員時打擾填充? –

4

我假設你正在編譯64位系統上。

在64位系統上,指針是8個字節。編譯器會將結構成員對齊到自然邊界,所以一個8字節的指針將從一個結構的偏移量開始,該結構是8個字節的倍數。

由於int只有4字節,插入4個字節「填充」的foo後編譯器,以便smap是一個8字節的邊界上。

編輯:儘管smap不是指針,而是std::map,但適用相同的邏輯。我不確定對象的確切規則是什麼,但同樣的事情正在發生。

怎麼辦?沒有。你的代碼非常好,編譯器只是讓你知道已經發生了。絕對沒有什麼可擔心的。 -Weverything意味着打開every possible warning,這對大多數所有的編譯來說可能都是過度的。

+0

謝謝!看起來你是對的;當我將'foo'改爲'long'時,警告消失。從你的答案我明白,這個警告的唯一有效含義是4個字節的內存將浪費在每個「Tester」實例上。 –

+0

要訪問'smap'成員,使用半指針大小的偏移量(4字節)訪問它的成本要比全指針大小(8字節)要昂貴,因爲我們從結構。 – bobobobo

+1

@DunPeal這不僅僅是內存將被_wasted_,而且如果你對這個數據結構進行二進制序列化,然後反序列化它,比如說在一臺32位的機器上,你會在這裏有這個神祕的填充可能不知道插入的編譯器。雖然你不會二進制序列化一個'std :: map',仍然。如果這是一些可以將自己簡化爲二進制序列化的原語,那麼你可能已經完成了。編譯器希望你知道'smap'會比你想象的更靠前4個字節。 – bobobobo