2016-03-06 45 views
3

我有一個特殊的問題,我有一個16位結構。在結構內部,在成員「x_pos」之後,取決於某個外部標誌,接下來的5位表示不同的東西。格式化在C位域內的聯盟

如果設置了外部標誌,它們是旋轉&縮放參數(rotscale_param),否則高兩位是水平和垂直翻轉位。

我試圖用聯合表示這個結構,但是當我這麼sizeof(屬性)時,我期待看到2個字節,但結果是4個字節。

我的代碼:

typedef unsigned short u16; 

struct attribute { 
    u16 x_pos : 9; 
    union { 
     u16 rotscale_param : 5; 
     struct { 
      u16 unused : 3; 
      u16 hflip : 1; 
      u16 vflip : 1; 
      }; 
    }; 
    u16 size : 2; 
}; 

如果有幫助,我試圖讓C代碼爲這種結構:http://problemkaputt.de/gbatek.htm#lcdobjoamattributes

這裏是一個:以上報價

OBJ Attribute 
    Bit Expl. 
    0-8 X-Coordinate   
    When Rotation/Scaling used: 
    9-13 Rotation/Scaling Parameter Selection 
    When Rotation/Scaling not used: 
    9-11 Not used 
    12 Horizontal Flip  
    13 Vertical Flip   
    14-15 OBJ Size 

來源潛在解決方案:

typedef struct attr_flag_set { 
    u16 x_pos : 9; 
    u16 rotscale_param : 5; 
    u16 size : 2; 
}; 

typedef struct attr_flag_unset { 
    u16 x_pos : 9; 
    u16 unused : 3; 
    u16 hflip : 1; 
    u16 vflip : 1; 
    u16 size : 2; 
}; 

union attribute_1 { 
    attr_flag_unset attr_unset; 
    attr_flag_set attr_set; 
}; 

但我不確定這是否是理想的解決方案。

+0

您不能假設您的編譯器會將您的位域結構打包到最緊密的空間中。檢查你的編譯器文檔,看看是否有可能導致這種情況發生的選項,但假設它會自動發生是錯誤的(如果連編譯器都允許的話)。 – mah

+0

@mah:除了union之外,難道你不認爲將一個結構中的9位字段和2位字段背靠背結合在一起是合理的嗎? –

+0

[c union和bitfields]的可能重複(http://stackoverflow.com/questions/3087057/c-union-and-bitfields) – mah

回答

1

要得到正確的包裝並不容易。
此外,該標準只允許在聲明屬於同一類型時打包位。
從ISO/IEC 9899 C99,6.7.2.1 Structure and union specifiers

實現可以分配任何可尋址的存儲單元大 足以容納一個位域。如果剩餘足夠的空間,那麼緊接在結構中的另一位字段之後的位字段將被打包爲 到相同單位的相鄰位中。如果剩餘空間不足, 是否將不合適的位字段放入下一個單元或是否與相鄰單元重疊是實現定義的。在一個單元內分配位域的順序(從高位到低位或從低位到高位)是實施定義的。未指定可尋址存儲單元的對齊方式爲 。

因此,當您在位域聲明中插入位域結構時,會破壞規則並保留另一個WORD進行存儲。
唯一的解決方法是申報恢復到2層不同結構的工會:

#pragma pack(1) 
union attribute 
{ 
    struct 
    { 
     u16 x_pos : 9; //See the homogeneous series of declarations 
     u16 rotscale_param : 5; 
     u16 size : 2; 
    } struct1; 
    struct 
    { 
     u16 x_pos : 9; //See the homogeneous series of declarations here also 
     u16 unused : 3; 
     u16 hflip : 1; 
     u16 vflip : 1; 
     u16 size : 2; 
    } struct2; 
}; 

這下,C99-C11爲我工作。
不要忘記指示編譯器打包數據。正如Mah讓我注意到的,這可以通過大多數編譯器的編譯指示或其他語言的特定語法來完成。而且在這種情況下,打包可以是編譯器依賴於那些邊界線應用程序。
P.S.還要考慮字節順序與編譯器相關,但主要取決於CPU永久性,因此,在小端機器上運行良好的代碼可能會在大端編程機器上失敗。
如果需要完全便​​攜式代碼,最佳做法是使用蒙版和輪班。

+0

是否有任何'#pragma'不是編譯器特定的情況? – mah

+0

你的回答很有意義,謝謝。這也是我想到的一個潛在的「解決方法」(在工會內部使用兩個結構),但如果我能夠完成最初的解決方案,我不確定它會是首選。非常感謝啓蒙! – user6025958

+0

@mah當然可以。感謝您的意見。其他編譯器可以使用不同的語法(即GCC使用'__attribute __(packed)')。但是,正如你已經概述的那樣,這類東西與HW很接近,所以一定要小心處理:) –

0

如果你想創建一個hflipvflip一個bitfiled,我覺得你的意思是做到以下幾點:

struct attribute { 
    u16 x_pos : 9; 
    union { 
     u16 rotscale_param : 5; 
     struct { 
      u16 unused : 3, 
       hflip : 1, 
       vflip : 1; 
     } hvflip; 
    }; 
    u16 size : 2; 
}; 

當你實際使用的是unusedhflipvflip創建位域出來的單一的unsigned short。如另一個答案中所示,通過使用3完成unsigned shorts來表示5有問題,完全忽略了位域的好處/目的。您只需要1 unsigned-short即可表示16位。

下面是一個簡單的例子:

#include <stdio.h> 

typedef unsigned short u16; 

struct attribute { 
    u16 x_pos : 9; 
    union { 
     u16 rotscale_param : 5; 
     struct { 
      u16 unused : 3, 
       hflip : 1, 
       vflip : 1; 
     } hvflip; 
    }; 
    u16 size : 2; 
}; 

void binprnpad (const unsigned long v, size_t sz); 

int main (void) { 

    struct attribute attr = { .x_pos = 0b101010101, 
           .rotscale_param = 0b10101, 
           .size = 0b10 }; 

    printf ("\n attr.x_pos (%3hu)   : ", attr.x_pos); 
    binprnpad (attr.x_pos, 9); 

    printf ("\n attr.rotscale_param (%3hu) : ", attr.rotscale_param); 
    binprnpad (attr.rotscale_param, 5); 

    printf ("\n attr.hvflip.unused (%3hu) : ", attr.hvflip.unused); 
    binprnpad (attr.hvflip.unused, 3); 
    printf ("\n attr.hvflip.hflip (%3hu) : ", attr.hvflip.hflip); 
    binprnpad (attr.hvflip.hflip, 1); 
    printf ("\n attr.hvflip.vflip (%3hu) : ", attr.hvflip.vflip); 
    binprnpad (attr.hvflip.vflip, 1); 

    printf ("\n attr.size (%3hu)   : ", attr.size); 
    binprnpad (attr.size, 2); 
    printf ("\n\n"); 

    return 0; 
} 

void binprnpad (const unsigned long v, size_t sz) 
{ 
    if (!sz) { fprintf (stderr, "error: invalid sz.\n"); return; } 
    if (!v) { putchar ('0'); return; } 

    while (sz--) 
     putchar ((v >> sz & 1) ? '1' : '0'); 
} 

輸出

$ ./bin/struct_nested_bin 

attr.x_pos (341)   : 101010101 
attr.rotscale_param (21) : 10101 
attr.hvflip.unused ( 5) : 101 
attr.hvflip.hflip ( 0) : 0 
attr.hvflip.vflip ( 1) : 1 
attr.size ( 2)   : 10 

讓我知道,如果你有任何問題。