2011-02-27 123 views
13

此問題特定於MSVC編譯器(特別是2008),但我對非編譯器特定的答案也很感興趣。對齊堆棧上的數據(C++)

我想弄清楚如何對齊堆棧上的字符緩衝區,基於一些任意類型的對齊方式。理想情況下,代碼將改爲:

__declspec(align(__alignof(MyType))) char buffer[16*sizeof(MyType)]; 

不幸的是,這是行不通

錯誤C2059:語法錯誤: '__builtin_alignof'

編譯器只是不喜歡嵌套語句。

我唯一的其他想法是這樣:

char buffer[16*sizeof(MyType)+__alignof(MyType)-1]; 
char * alignedBuffer = (char*)((((unsigned long)buffer) + __alignof(MyType)-1)&~(__alignof(MyType)-1)); 

有誰知道一個更好的方式?看起來declspec的東西應該可以工作,我只是有語法錯誤或什麼的?

感謝您的閱讀:)

+1

'... __alignof [結構]是結構中最大元素的對齊要求。「(http://msdn.microsoft.com/en-us/library/45t0s5f4(v=vs.71) .aspx) – 2011-02-27 16:35:19

+0

如果您嘗試節省空間並壓縮數據,爲什麼不直接對齊(1)? – 2011-02-27 16:35:49

+2

這不是關於節省空間,而是關於爲任意類型分配堆棧空間以及尊重該類型的對齊要求。第二塊代碼就是這麼做的,我只是想知道是否(通常情況下)有更好的方法來做到這一點。 – JBeFat 2011-02-27 17:42:28

回答

3

更新

檢查羅伯特騎士的回答!使用C++ 11,但比這更乾淨...


原來的答案

怎麼樣這個討厭的黑客:

namespace priv { 

#define PRIVATE_STATICMEM(_A_) \ 
    template <size_t size> \ 
    struct StaticMem<size,_A_> { \ 
     __declspec(align(_A_)) char data[size]; \ 
     void *operator new(size_t parSize) { \ 
     return _aligned_malloc(parSize,_A_); \ 
     } \ 
     void operator delete(void *ptr) { \ 
     return _aligned_free(ptr); \ 
     } \ 
    }; 

    template <size_t size, size_t align> struct StaticMem {}; 
    template <size_t size> struct StaticMem<size,1> {char data[size];}; 

    PRIVATE_STATICMEM(2) 
    PRIVATE_STATICMEM(4) 
    PRIVATE_STATICMEM(8) 
    PRIVATE_STATICMEM(16) 
    PRIVATE_STATICMEM(32) 
    PRIVATE_STATICMEM(64) 
    PRIVATE_STATICMEM(128) 
    PRIVATE_STATICMEM(256) 
    PRIVATE_STATICMEM(512) 
    PRIVATE_STATICMEM(1024) 
    PRIVATE_STATICMEM(2048) 
    PRIVATE_STATICMEM(4096) 
    PRIVATE_STATICMEM(8192) 

} 

template <typename T, size_t size> struct StaticMem : public priv::StaticMem<sizeof(T)*size,__alignof(T)> { 
    T *unhack() {return (T*)this;} 
    T &unhack(size_t idx) {return *(T*)(data+idx*sizeof(T));} 
    const T &unhack() const {return *(const T*)this;} 
    const T &unhack(size_t idx) const {return *(const T*)(data+idx*sizeof(T));} 
    StaticMem() {} 
    StaticMem(const T &init) {unhack()=init;} 
}; 

看起來嚇人,但你需要的一切,只有一次(在一些隱藏的很好的頭文件:)最好)。然後,您可以通過以下方式使用它:

StaticMem<T,N> array; //allocate an uninitialized array of size N for type T 
array.data //this is a raw char array 
array.unhack() //this is a reference to first T object in the array 
array.unhack(5) //reference to 5th T object in the array 

StaticMem<T,N> array;可以出現在代碼中,還可以作爲一些較大的類的成員(這是我如何使用這個技巧),並當上了分配也應該表現正常堆。

錯誤修復:

的例子的第6行:char data[_A_]校正成char data[size]

+0

這是一個非常有趣的答案,謝謝:)我從來沒有想過爲每個可能的對齊值手動部分專門化模板。我不明白的一件事是爲什麼你必須定義你自己的新/刪除操作符?我的第一個想法是,它確保數據在堆上分配時正確對齊,但是不會爲標準新對你做什麼(因爲你在那裏有__declspec(align(_A_)))? – JBeFat 2011-02-27 18:25:41

+0

我不知道標準中關於'new'運算符的說法,但是我在Visual Studio 2008中進行的測試似乎表明數據不是必須對齊的。 (我剛剛重新測試了一下,以確認) – CygnusX1 2011-02-27 18:40:41

+0

是的,我知道的所有標準malloc都是對齊的16.固定的,硬編碼的。儘管有一些擴展。 cf'_aligned_malloc' – 2014-12-22 08:55:44

3

你肯定MyType是一個有效的整數次冪?

__declspec(align(#)) declarator 

#是對準值。有效條目是從 1到8192(字節)的整數次冪,例如2,4,8, 16,32或64.聲明符是您聲明爲對齊的數據 。

- align (C++)

+0

在我的測試案例中,它是。在一般情況下,它不一定是: '__alignof(char)'將返回1.我想這是我的第一種方法不起作用的足夠好的理由。 – JBeFat 2011-02-27 16:15:21

+2

@JBeFat:'1'是'2'的整數次冪......它是2 ** 0。 – 2011-02-27 17:14:31

0

,如果你想聲明在棧上對齊的數據,您不需要執行任何額外的「黑客」。編譯器會照顧到這一點。

如果你希望它是一個字符數組後,只需將其轉換爲char*

試試下面的測試例子來證實:

#include <stdio.h> 

struct UnalignedX { 
    int x; 
}; 

__declspec(align(128)) struct AlignedX { 
    int x; 
}; 

int main() { 
    UnalignedX arr[5]; 
    AlignedX aarr[5]; 
    printf("UnalignedX: %x %x\n",arr,arr+1); 
    printf("AlignedX: %x %x\n",aarr,aarr+1); 
    char *final=(char*)aarr; //this becomes the char array that you asked for 
    return 0; 
}; 

在我的電腦我得到的輸出:

UnalignedX: 14fe68 14fe6c 
AlignedX: 14fb80 14fc00 

在堆上分配數據時,您必須小心對齊(通過mallocnew

__declspec(align(N))期望N成爲文字。它必須是一個數字,而不是函數調用。

+1

我更想將數據聲明爲char,並在之後進行轉換。我基本上想在棧上構造一個特定類型的數組,而不調用它們的構造函數。原則上,這與轉換char緩衝區一樣簡單,但我真的很喜歡棧上的alloc來尊重緩衝區將用於的類型的對齊要求。 – JBeFat 2011-02-27 17:40:14

1

如何ALLOCA()? (或者更具體地說,對於MSVC2008,_malloca())?

分配堆棧上的內存。這是一個帶有安全增強功能的_alloca版本,如CRT中的安全增強所述。

對齊行爲不是跨編譯器標準化的,但對於這一個...

的_malloca例程返回一個空指針所分配的空間,這是保證用於存儲任何適當對準對象的類型。

+0

哇,很酷。這就是我要找的。我唯一不瞭解的是它如何保證空間適合存儲任何類型的對象。我測試了_malloca,看起來好像是16byte對齊的返回指針。如果我有一個__dllspec(align(128))的結構呢?也許有一些我不太瞭解對齊要求? – JBeFat 2011-02-27 18:47:16

+1

我認爲「適當對齊」意味着正確執行就足夠了。如果你需要比這更粗略地排列,出於優化的原因,我認爲alloca()不會幫助你。 – 2011-02-27 18:53:01

1

有同樣的問題。 可以混搭宏(哦恐怖),在一個安全的方式結合align__alignof

// `align` und `__alignof` cannot be combined - hence this workaround where you also have to specify the alignment manually (but checked) 
#define ALIGN_FOR_TYPE(TypeName, TypeAlignment)       \ 
    const size_t ALIGN_FOR_TYPE_alignOf##TypeName = __alignof(TypeName); \ 
    BOOST_STATIC_ASSERT(ALIGN_FOR_TYPE_alignOf##TypeName == TypeAlignment); \ 
    __declspec(align(TypeAlignment))         \ 
/**/ 

ALIGN_FOR_TYPE(MyStructType, 4) char StructBuffer[sizeof(MyStructType)]; 
+0

這是如何回答這個問題的? IIUC,OP想要完全避免明確說明他的類型對齊。 – 2014-03-04 18:31:10

+0

@Ofek - 它解決了這個問題,因爲如果指定了錯誤的對齊方式,將會出現編譯器錯誤。對於一次性的解決方案來說,它看起來比其他黑客更簡單,並且它仍然安全,但如果對齊更改,顯然可能需要觸摸代碼。 – 2014-03-04 19:08:52

5

可以一起使用std::aligned_storagestd::alignment_of作爲替代。

#include <type_traits> 

template <class T, int N> 
struct AlignedStorage 
{ 
    typename std::aligned_storage<sizeof(T) * N, std::alignment_of<T>::value>::type data; 
}; 

AlignedStorage<int, 16> myValue; 

這是由MSVC 2008及以上版本支持的。如果您需要將其移植到其他非C++ 11編譯器,則可以使用std::tr1::aligned_storagestd::tr1::alignment_of<tr1/type_traits>標題。

在上面的代碼中,AlignedStorage<T>::data將是T和T * N大小合適對齊的POD類型(MSVC和GCC中的char []數組)。

+0

我沒有意識到這一點(或者當我回答時它可能不存在?)。明確地比我的解決方案更好! – CygnusX1 2015-10-29 11:23:29