class中對齊導致的數據問題

首先來看一個代碼,來找找其中潛在的問題是什麼(並不一定有問題,但一定是有潛在問題)?

<code>// ...
static constexpr std::uint32_t kMaxColorAttachments = 4u;
// ...
struct FramebufferCacheQuery
{
size_t operator()() const
{
static XXHash64 _hashFunc(0x21378732);
return (size_t) (_hashFunc.hash(this, sizeof(FramebufferCacheQuery), 0));
}

VkRenderPass renderPass = nullptr;
VkImageView attachments[kMaxColorAttachments] = {nullptr, nullptr, nullptr, nullptr};
std::uint32_t width = 0;
std::uint32_t height = 0;
std::uint32_t layers = 1;
};/<code>

乍看這段代碼是沒有什麼問題的,聲明一個仿函數對象,調用時對其自身進行hash,所有的值都已經進行了正確的初始化,因此應該是可以正常工作的。

下面為了找出問題,我們逐步進行分析。首先FramebufferCacheQuery這個結構體的大小是多少呢,即sizeof(FramebufferCacheQuery) = ??另外從初始化方式上可以看出VkRenderPass和VkImageView都是指針類型。

這裡就不弔大家胃口了,結果有(至少)兩種,為什麼是至少,後面再給出說明:

<code>sizeof(FramebufferCacheQuery) == 32;
sizeof(FramebufferCacheQuery) == 56;/<code>

為什麼會有兩種結果,考慮一下sizeof(void*)的大小,在32位平臺和64位平臺上分別是4和8。在32位平臺上sizeof(...)==32顯而易見的,另外在64平臺上結果為56也是很容易理解的,在sizeof(void*)==8時,結構體採用了8byte進行對齊。

上面的分析依然沒有給出潛在問題的所在,這裡就直接說明了,在64位平臺上sizeof(...) == 56,但是真正能被對象操作的數據只有52個bytes,另外4個byte(結構體的尾部)是編譯器自動進行填充的,具體填充的是什麼值,C++標準中並沒有明確定義,完全依賴於編譯器的實現,極大的可能是出現一些隨機值。因此在64位平臺上可能會出現設置相同數據的對象結果出現不同的hash值。

如何解決這個問題?

  1. 手動pack,但是這種方式不具有靈活性和可擴展性
<code>struct FramebufferCacheQuery
{
//...
#ifdef ARCH_64
char __padding[4] = {0};
#endif
};/<code>
  1. 使用編譯器預處理指令#pragma
<code>#pragma pack(push)
#pragma pack(1)
struct FramebufferCacheQuery
{
// ...
};
#pragma pack(pop)/<code>

#pragma pack(1)的意思是strct/class/union等使用1byte進行對齊,sizeof(...) == 52。

注意,使用1byte對齊會影響到編譯器和CPU對代碼性能的優化,除非有明確的認識,否則不要大量使用。


C++中struct/class中對齊導致的數據問題


分享到:


相關文章: