簡體   English   中英

如何將 32 位無符號整數分配給包含 32 位的位字段

[英]How to assign a 32-bit unsigned integer to a bit field containing 32 bits

我正在嘗試創建一個總共有 32 位的位域結構,但是當我嘗試為其分配一個 32 位的數字時,我收到此錯誤:

從 'unsigned int' 到位域的隱式截斷將值從 4278190080 更改為 0

這是我的結構以及我如何嘗試使用它

struct Color32 {
    uint32_t a : 8;
    uint32_t r : 8;
    uint32_t g : 8;
    uint32_t b : 8;
};


Color32 BLACK = {0xFF000000}; // this line has the compilation error

我看到有關位字段分配的其他問題,但它們似乎都使用按位運算來設置各個字段。

還有這個參考資料,其中包含以下示例,這似乎與我使用它的方式相同,只有我的無法編譯:

#include <iostream>
struct S {
 // three-bit unsigned field,
 // allowed values are 0...7
 unsigned int b : 3;
};
int main()
{
    S s = {6};
    ++s.b; // store the value 7 in the bit field
    std::cout << s.b << '\n';
    ++s.b; // the value 8 does not fit in this bit field
    std::cout << s.b << '\n'; // formally implementation-defined, typically 0
}

您可以在此處使用聚合初始化

Color32 BLACK = {0xFF, 0x00, 0x00, 0x00};

順便說一句,我建議將您的Color32結構修改為以下內容,這與指定成員的位字段具有相同的效果

struct Color32 {
    uint8_t a;
    uint8_t r;
    uint8_t g;
    uint8_t b;
};

像這樣的事情會給你兩全其美的:

    struct Color32 {
    union {
        uint32_t color;
        struct {
            uint32_t b : 8;
            uint32_t g : 8;
            uint32_t r : 8;
            uint32_t a : 8;
        };
    };
};

// will construct using single value
Color32 test{ 0x01020304 };
Color32 black{0xff000000 };

// can assign to individual fields
test.a = 0x04;
test.r = 0x03;
test.g = 0x02;
test.b = 0x01;

// can assign to the whole value like this.
test.color = 0xff000000;
test.color = black.color;

一個問題是結構中 a、b、g、r 的順序可能取決於您的特定編譯器。 對於 VS2017 編譯到 windows 目標,顯示的順序將產生預期的結果。 我相信可能有辦法以某種方式強制命令,但我不熟悉如何去做。

位域與否,您的類型有四個成員,而不是一個。

你似乎試圖把它當作一個聯合來對待。

像使用任何其他類型一樣單獨初始化每個成員,或者切換到聯合(然后像許多人一樣依賴類型雙關語,但通常需要注意)。


你給出的反例不一樣,因為它是一個UDT,在初始化器中只有一個成員和一個值; 由於匹配的成員數量,那里一切都很好。

在深入研究該主題之后,我發現如果沒有按位運算符和有效的構造函數,多個位字段是沒有用的,因為它在很大程度上取決於操作系統。

答案在 windows 7 上的 cygwin ( -Wno-unused-variable -O0 -ggdb flags ) 上進行測試

版本 1: union

這是沒有任何位域的基本實現,最常見的 4 字節顏色空間實現。

#include <iostream>  

union c_space32{
    uint32_t space;
    uint8_t channels[4];
};

int main(){

    { // just a anonymous scope to keep things clear 
        union c_space32 temp = {0xff00fe32};
        std::cout << "sizeof : " << sizeof( union c_space32 ) << "\n\n";
        std::cout << (int)temp.channels[1] << "\t" << std::hex << temp.space <<  "\n";
        ++temp.channels[1];
        std::cout << (int)temp.channels[1] << "\t" << std::hex << temp.space <<  "\n";
        ++temp.channels[1];
        std::cout << (int)temp.channels[1] << "\t" << std::hex << temp.space <<  "\n";
    }

return 0;}  

union 表現為正常的顏色空間,並且 union 的每個uint8_t部分表現為唯一的字節,因此c_space32.channels中值的整體變化不會影響字節范圍之外的c_space32.space值。 這是我得到的輸出。

sizeof : 4
fe  ff00fe32
ff  ff00ff32
0   ff000032  

版本 2: bit-fields

位域的問題(在某些情況下缺乏文檔)是它們的大小很容易改變,並且字節順序取決於操作系統,因此位域結構背后的本機邏輯可以逃避我們的人類邏輯。 讓我給你一些例子,供未來希望致力於這個主題的人/女孩使用。

#include <iostream> 
#include <bitset> 

struct temp1{
    uint8_t a:1;
    temp1(uint8_t val){ // just so i can assign it
        this->a = (val & 0x1 ); // this is needed to avoid truncated warning
    }
};

int main(){
    struct temp1 t1 = 3;
    uint8_t *ptr = (uint8_t *)&t1;
    std::cout << sizeof(struct temp1) << std::endl; // size of 1 byte
    std::cout << std::bitset<8>( *ptr ) << std::endl; // 0000-0001 position of our bitfield
return 0;}

所以在這種情況下sizeof(struct temp1)返回大小為 1 byte 將我們的位域的位置放在最右邊。 這就是文檔開始進入 MIA 的地方。

#include <iostream> 
#include <bitset> 

struct temp2{
    uint8_t a:1;
    uint8_t b:1;
    uint8_t c:1;
    temp2(int VAL){ // just so i can assign it
        this->a = (VAL & 0x1 );
        this->b = 0;
        this->c = (VAL >> 2 ) & 0x1;
    }
};

int main(){
    struct temp2 t1 = 0xf;
    uint8_t *ptr = (uint8_t *)&t1;
    std::cout << sizeof(struct temp2) << std::endl; // size of 1
    std::cout << std::bitset<8>( *ptr ) << std::endl; // 0000-0101
return 0;}

在這種情況下, constructor必須的,因為計算機不知道您想要如何構造數據。 當然,在我們的邏輯中,如果我們排列位,則分配它們與它們共享內存相同是合乎邏輯的。 但問題是計算機不會為我們做bitwise operators 當然這些位是有序的並且自然排列,但是計算機只是抓取一些位並將其定義為一個唯一的變量,您選擇在該變量中放置什么取決於您。

如果我們超出了unit memory size (字節)的范圍,操作系統就會開始干擾我們的工作。

#include <iostream> 
#include <bitset> 

struct temp3{
    bool b0:1;
    bool b1:1;
    bool b2:1;
    bool b3:1;
    bool b4:1;
    bool b5:1;
    bool b6:1;
    bool b7:1;

    temp3( int a ){
        this->b0 = ( a & 0x1 );
        this->b1 = ( a & 0x2 );
        this->b2 = ( a & 0x4 );
        this->b3 = ( a & 0x8 );
        this->b4 = ( a & 0x10 );
        this->b5 = ( a & 0x20 );
        this->b6 = ( a & 0x40 );
        this->b7 = ( a & 0x80 );
    }

};

int main(){
    struct temp3 t1 = 0xc3;
    uint8_t *ptr = (uint8_t *)&t1;
    std::cout << sizeof(struct temp3) << std::endl; // still size of 1
    std::cout << std::bitset<8>( *ptr ) << std::endl; // 1100-0011
return 0;}  

當我們超過字節大小時:

#include <iostream> 
#include <bitset> 
struct temp4{
    bool b0:1;
    bool b1:1;
    bool b2:1;
    bool b3:1;
    bool b4:1;
    bool b5:1;
    bool b6:1;
    bool b7:1;
    bool b8:1;

    temp4( int a ){
        this->b0 = ( a & 0x1 );
        this->b1 = ( a & 0x2 );
        this->b2 = ( a & 0x4 );
        this->b3 = ( a & 0x8 );
        this->b4 = ( a & 0x10 );
        this->b5 = ( a & 0x20 );
        this->b6 = ( a & 0x40 );
        this->b7 = ( a & 0x80 );
        this->b8 = ( a & 0x100 );
    }

};

int main(){
    struct temp4 t1 = 0x1c3;
    uint16_t *ptr = (uint16_t *)&t1;
    std::cout << sizeof(struct temp4) << std::endl; // size of 2
    std::cout << std::bitset<16>( *ptr ) << std::endl; // 0000-0000 1100-0011
    std::cout << t1.b8 << std::endl; // still returns 1
    std::cout << "\n\n";

    union t_as{
        uint16_t space;
        temp4 data;
        uint8_t bytes[2];
    };

    union t_as t2 = {0x1c3};
    //11000011-00000001
    std::cout << std::bitset<8>( t2.bytes[0] ) << "-"  << std::bitset<8>( t2.bytes[1] ) << std::endl;
return 0;}  

這里發生了什么? 由於我們添加了另一個bool bit-field我們的結構增長了 1 個字節(因為 bool 是 1 個字節),並且我們的 16 位指針不顯示最后一個b8 - 但聯合顯示。 問題是操作系統接管了,在這種情況下,由於先天的操作系統字節順序,將最后一點卡在了我們的原始內存后面。 正如您在 union 中看到的那樣,仍然讀取字節,但順序不同。

因此,當超過字節大小時,適用正常的操作系統規則。

結論與答案

struct half_opacity{
    uint8_t alpha:4;
    uint8_t red;
    uint8_t green;
    uint8_t blue;
    half_opacity(int a){
        this->alpha = ( a >> 24 )&0xf;
        this->red   = ( a >> 16 )&0xff;
        this->green = ( a >> 8  )&0xff;
        this->blue  = ( a & 0xff );
    }
    operator uint32_t(){
        return      ( this->alpha << 24 )
                |   ( this->red   << 16 )
                |   ( this->green << 8 )
                |   this->blue;
    }
};

{
    struct half_opacity c_space = 0xff00AABB;
    std::cout << "size of : " << sizeof(struct half_opacity) << std::endl; //size of : 4
    std::cout << std::hex << (uint32_t)c_space << std::endl; // 0x0f00AABB  
}

因此,除非您打算將原始通道信任為某個位大小,否則我強烈建議使用union方法,因為將 32 位整數拆分為帶有bit-fields單個字節沒有任何額外的好處。 關於bit fields的主要事情是您需要將它們拆分並構建然后像任何其他整數域一樣備份 - bit shifts通常會繞過整個操作系統字節序。

您收到的截斷警告是由於您的結構中有多個成員,並且 struct 自然地分配了第一個成員,並且由於您添加的bit field超出了bit field處理能力,編譯器警告您某些數據將丟失。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM