如何将 32 位无符号整数分配给包含 32 位的位字段

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

本文关键字:包含 字段 无符号整数 分配      更新时间:2023-10-16

我正在尝试创建一个总共有 32 位的位字段结构,但是当我尝试为其分配一个 32 位数字时,出现此错误:

"无符号 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 的顺序可能取决于您的特定编译器。 对于编译到 Windows 目标的 VS2017,显示的顺序将产生预期的结果。 我相信可能有一种方法可以以某种方式强制下令,但我不熟悉如何做到这一点。

无论是否位字段,您的类型都有四个成员,而不是一个。

你似乎试图把它当作一个工会。

像使用任何其他类型一样单独初始化每个成员,或者切换到联合(然后像许多人一样依赖类型双关语,但通常需要注意)。


你给出的反例是不一样的,因为它是一个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 ) << "nn";
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;}  

联合的行为与正常的色彩空间一样,联合的每个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)返回的大小为 1byte。我们的位字段的位置是最右边的。这就是文档开始进入 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 << "nn";
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;}  

这里发生了什么?由于我们添加了另一个boolbit-field我们的结构增长了 1 个字节(因为 bool 是 1 个字节),并且我们的 16 位指针没有显示最后的b8- 但联合确实如此。问题是操作系统接管了,在这种情况下,由于与生俱来的操作系统字节序,将最后一点卡在我们的原始内存后面。正如你在联合中看到的,字节仍然被读取,但顺序不同。

因此,当超过字节大小时,将适用正常的操作系统规则。

结论和答案

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经常绕过整个操作系统字节序。

您得到的截断警告是由于结构中的多个成员,并且结构自然分配了第一个成员,并且由于您添加的比bit field可以处理的更多,编译器警告您将丢失一些数据。