如何将 32 位无符号整数分配给包含 32 位的位域
How to assign a 32-bit unsigned integer to a bit field containing 32 bits
我正在尝试创建一个总共有 32 位的位域结构,但是当我尝试为其分配一个 32 位的数字时,出现此错误:
Implicit truncation from 'unsigned int' to bit-field changes value from 4278190080 to 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
我看到 other questions 围绕位域赋值,但它们似乎都使用按位操作来设置各个域。
还有this reference里面有下面的例子,好像和我用的一样,只有我的编译不通过:
#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
}
你可以在这里使用aggregate initialization
Color32 BLACK = {0xFF, 0x00, 0x00, 0x00};
顺便说一下,我建议将您的 Color32
结构修改为以下内容,这与指定成员的位字段具有相同的效果
struct Color32 {
uint8_t a;
uint8_t r;
uint8_t g;
uint8_t b;
};
无论是否是位域,您的类型有 四个 个成员,而不是一个。
你似乎想把它当作一个联合。
像使用任何其他类型一样单独初始化每个成员,或切换到联合(然后像许多人一样依赖类型双关,但有通常的注意事项)。
你给出的反例不一样,因为它是一个UDT,在初始化器中只有一个成员和一个值;由于给定的成员数量匹配,所以那里一切都很好。
深入研究该主题后,我发现如果没有按位运算符和有效的构造函数,多个位字段就没有用,这在很大程度上取决于操作系统。
答案在 windows 7
的 cygwin(-Wno-unused-variable -O0 -ggdb 标志)上测试
Version 1: union
这是没有任何位域的基本实现,最常见的 4 字节颜色实现 space。
#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;}
联合表现为正常颜色 space,联合的每个 uint8_t
部分表现为唯一字节,因此 c_space32.channels
中值的整体变化不会影响值c_space32.space
字节范围之外。这是我得到的输出。
sizeof : 4
fe ff00fe32
ff ff00ff32
0 ff000032
Version 2: bit-fields
位字段的问题(在某些情况下缺乏文档)是它们的大小很容易改变,字节顺序取决于 OS 因此位字段结构背后的本机逻辑可以逃避我们的人的逻辑。让我举一些例子给未来 guys/gals 希望努力研究这个话题的人。
#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)
returns 大小为 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
(字节) 的范围,OS 就会开始干扰我们的工作。
#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
- 但联合显示。问题是 OS 接管了,在这种情况下,最后一点卡在了我们原始内存的后面 - 由于天生的 OS 字节序。正如您在 union 中看到的,字节仍然被读取,但顺序不同。
So when exceeding the byte size, normal OS rules apply.
结论和答案
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
通常会绕过整个 OS 字节序的事情。
您收到的截断警告是由于您的结构中有多个成员,并且结构自然地分配了第一个,并且由于您添加的内容超过了 bit field
可以处理的程度,编译器警告您某些数据将迷路了。
像这样的东西会让你两全其美:
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,显示的顺序将产生预期的结果。我相信可能有办法以某种方式强制执行命令,但我不熟悉该怎么做。
我正在尝试创建一个总共有 32 位的位域结构,但是当我尝试为其分配一个 32 位的数字时,出现此错误:
Implicit truncation from 'unsigned int' to bit-field changes value from 4278190080 to 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
我看到 other questions 围绕位域赋值,但它们似乎都使用按位操作来设置各个域。
还有this reference里面有下面的例子,好像和我用的一样,只有我的编译不通过:
#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
}
你可以在这里使用aggregate initialization
Color32 BLACK = {0xFF, 0x00, 0x00, 0x00};
顺便说一下,我建议将您的 Color32
结构修改为以下内容,这与指定成员的位字段具有相同的效果
struct Color32 {
uint8_t a;
uint8_t r;
uint8_t g;
uint8_t b;
};
无论是否是位域,您的类型有 四个 个成员,而不是一个。
你似乎想把它当作一个联合。
像使用任何其他类型一样单独初始化每个成员,或切换到联合(然后像许多人一样依赖类型双关,但有通常的注意事项)。
你给出的反例不一样,因为它是一个UDT,在初始化器中只有一个成员和一个值;由于给定的成员数量匹配,所以那里一切都很好。
深入研究该主题后,我发现如果没有按位运算符和有效的构造函数,多个位字段就没有用,这在很大程度上取决于操作系统。
答案在 windows 7
的 cygwin(-Wno-unused-variable -O0 -ggdb 标志)上测试Version 1:
union
这是没有任何位域的基本实现,最常见的 4 字节颜色实现 space。
#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;}
联合表现为正常颜色 space,联合的每个 uint8_t
部分表现为唯一字节,因此 c_space32.channels
中值的整体变化不会影响值c_space32.space
字节范围之外。这是我得到的输出。
sizeof : 4
fe ff00fe32
ff ff00ff32
0 ff000032
Version 2:
bit-fields
位字段的问题(在某些情况下缺乏文档)是它们的大小很容易改变,字节顺序取决于 OS 因此位字段结构背后的本机逻辑可以逃避我们的人的逻辑。让我举一些例子给未来 guys/gals 希望努力研究这个话题的人。
#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)
returns 大小为 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
(字节) 的范围,OS 就会开始干扰我们的工作。
#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
- 但联合显示。问题是 OS 接管了,在这种情况下,最后一点卡在了我们原始内存的后面 - 由于天生的 OS 字节序。正如您在 union 中看到的,字节仍然被读取,但顺序不同。
So when exceeding the byte size, normal OS rules apply.
结论和答案
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
通常会绕过整个 OS 字节序的事情。
您收到的截断警告是由于您的结构中有多个成员,并且结构自然地分配了第一个,并且由于您添加的内容超过了 bit field
可以处理的程度,编译器警告您某些数据将迷路了。
像这样的东西会让你两全其美:
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,显示的顺序将产生预期的结果。我相信可能有办法以某种方式强制执行命令,但我不熟悉该怎么做。