位字段的联合是否有意义
Does union of bit fields make any sense
我最近在我们的应用程序中发现了一个对我来说没有任何意义的第三方代码片段。首先让我感到困惑的是,它已经投入生产至少 10 年并且似乎有效。它基本上是一个 union
个位域:
union
{
unsigned longitude : 2; //!< status of the longitude (offset: 0)
unsigned latitude : 2; //!< status of the latitude (offset: 2)
unsigned xPosition : 2; //!< status of the x position relative to starting position (offset: 4)
unsigned yPosition : 2; //!< status of the y position relative to starting position (offset: 6)
// ... Many more 2-bit fields... Total 26 fields
unsigned reserved : 12;
unsigned long status[2]; //!< raw status data
} status;
我很确定这是一个错误,作者真正想写的是:
union
{
struct
{
unsigned longitude : 2; //!< status of the longitude (offset: 0)
unsigned latitude : 2; //!< status of the latitude (offset: 2)
unsigned xPosition : 2; //!< status of the x position relative to starting position (offset: 4)
unsigned yPosition : 2; //!< status of the y position relative to starting position (offset: 6)
// ... Many more 2-bit fields... Total 26 fields
unsigned reserved : 12;
};
unsigned status[2]; //!< raw status data (should not be long!)
} status;
它能正常工作的原因是实际上只使用了这 26 位字段中的一个!
但是这个bug提出了几个问题:
- 编译器是否需要对所有字段使用相同
unsigned
的相同前 2 位(尽管评论怎么说)?
union
位字段有实际用途吗?我想不出在任何情况下这都有意义。
- 如果这没有任何意义,为什么我们使用的编译器(无论是 Clang、GCC 还是 MSVC)都没有发出任何警告?
Is the compiler required to use the same first 2 bits of the same unsigned for all fields (despite what the comments say)?
没有。 C 2018 6.7.2.1 说“一个实现可以分配任何大到足以容纳 bit-field 的可寻址存储单元......一个单元内 bit-fields 的分配顺序(high-order 到 low-order 或 low-order 到 high-order) 是 implementation-defined…”
它并没有说可寻址存储单元对于所有相同大小的 bit-field 都是相同的。如果是这样,那么所有相同大小的联合 bit-field 成员都必须使用相同的位,当然任何合理的 C 实现都会这样做。
但是,请考虑 bit-field 不同大小的文件。编译器为 2 位的 bit-field 分配一个 one-byte 存储单元,为 17 位的 bit-field 分配一个 four-byte 存储单元是合理的。如果它是一个 little-endian 系统并将位放在 high-order 到 low-order 中,那么 2 位字段将在位 27 中,并且26 的字节 0,17 位字段将在字节 3 和 2 的所有位(位 231 到 2[= four-bytelittle-endian存储单元的20=]16和字节1的bit 27(bit 215 的存储单元)。所以这两个工会成员之间不会有重叠。
Is there any real usage for a union of bit fields? I cannot think of any situation where this would make sense.
当然,我的数据结构中可能有一些字段有时需要存储 17 位的 fromitz 编号,而其他时候需要存储 13 位的 gizmo 编号。联合体最初是为了存储某种东西,而不是为了将一种类型的位重新解释为另一种类型。
我很好奇所以我把它编码了。当然,这是一个等待发生的错误。
#include <stdio.h>
#include <string.h>
union
{
unsigned longitude : 2; //!< status of the longitude (offset: 0)
unsigned latitude : 2; //!< status of the latitude (offset: 2)
unsigned xPosition : 2; //!< status of the x position relative to starting position (offset: 4)
unsigned yPosition : 2; //!< status of the y position relative to starting position (offset: 6)
// ... Many more 2-bit fields... Total 26 fields
unsigned reserved : 12;
unsigned long status[2]; //!< raw status data
} status;
int main( int argc, char **argv) {
memset( &status, 0x00, sizeof(status) );
status.longitude = 0x02;
printf("Union Size = %lu\n", sizeof(status));
printf("status.longitude = %x\n", status.longitude );
printf("status.latitude = %x\n", status.latitude );
}
设置 status.longitude = 0x02;
有效地将 ūnion
中的所有其他变量设置为相同的值。
输出为:
Union Size = 16
status.longitude = 2
status.latitude = 2
我最近在我们的应用程序中发现了一个对我来说没有任何意义的第三方代码片段。首先让我感到困惑的是,它已经投入生产至少 10 年并且似乎有效。它基本上是一个 union
个位域:
union
{
unsigned longitude : 2; //!< status of the longitude (offset: 0)
unsigned latitude : 2; //!< status of the latitude (offset: 2)
unsigned xPosition : 2; //!< status of the x position relative to starting position (offset: 4)
unsigned yPosition : 2; //!< status of the y position relative to starting position (offset: 6)
// ... Many more 2-bit fields... Total 26 fields
unsigned reserved : 12;
unsigned long status[2]; //!< raw status data
} status;
我很确定这是一个错误,作者真正想写的是:
union
{
struct
{
unsigned longitude : 2; //!< status of the longitude (offset: 0)
unsigned latitude : 2; //!< status of the latitude (offset: 2)
unsigned xPosition : 2; //!< status of the x position relative to starting position (offset: 4)
unsigned yPosition : 2; //!< status of the y position relative to starting position (offset: 6)
// ... Many more 2-bit fields... Total 26 fields
unsigned reserved : 12;
};
unsigned status[2]; //!< raw status data (should not be long!)
} status;
它能正常工作的原因是实际上只使用了这 26 位字段中的一个! 但是这个bug提出了几个问题:
- 编译器是否需要对所有字段使用相同
unsigned
的相同前 2 位(尽管评论怎么说)? union
位字段有实际用途吗?我想不出在任何情况下这都有意义。- 如果这没有任何意义,为什么我们使用的编译器(无论是 Clang、GCC 还是 MSVC)都没有发出任何警告?
Is the compiler required to use the same first 2 bits of the same unsigned for all fields (despite what the comments say)?
没有。 C 2018 6.7.2.1 说“一个实现可以分配任何大到足以容纳 bit-field 的可寻址存储单元......一个单元内 bit-fields 的分配顺序(high-order 到 low-order 或 low-order 到 high-order) 是 implementation-defined…”
它并没有说可寻址存储单元对于所有相同大小的 bit-field 都是相同的。如果是这样,那么所有相同大小的联合 bit-field 成员都必须使用相同的位,当然任何合理的 C 实现都会这样做。
但是,请考虑 bit-field 不同大小的文件。编译器为 2 位的 bit-field 分配一个 one-byte 存储单元,为 17 位的 bit-field 分配一个 four-byte 存储单元是合理的。如果它是一个 little-endian 系统并将位放在 high-order 到 low-order 中,那么 2 位字段将在位 27 中,并且26 的字节 0,17 位字段将在字节 3 和 2 的所有位(位 231 到 2[= four-bytelittle-endian存储单元的20=]16和字节1的bit 27(bit 215 的存储单元)。所以这两个工会成员之间不会有重叠。
Is there any real usage for a union of bit fields? I cannot think of any situation where this would make sense.
当然,我的数据结构中可能有一些字段有时需要存储 17 位的 fromitz 编号,而其他时候需要存储 13 位的 gizmo 编号。联合体最初是为了存储某种东西,而不是为了将一种类型的位重新解释为另一种类型。
我很好奇所以我把它编码了。当然,这是一个等待发生的错误。
#include <stdio.h>
#include <string.h>
union
{
unsigned longitude : 2; //!< status of the longitude (offset: 0)
unsigned latitude : 2; //!< status of the latitude (offset: 2)
unsigned xPosition : 2; //!< status of the x position relative to starting position (offset: 4)
unsigned yPosition : 2; //!< status of the y position relative to starting position (offset: 6)
// ... Many more 2-bit fields... Total 26 fields
unsigned reserved : 12;
unsigned long status[2]; //!< raw status data
} status;
int main( int argc, char **argv) {
memset( &status, 0x00, sizeof(status) );
status.longitude = 0x02;
printf("Union Size = %lu\n", sizeof(status));
printf("status.longitude = %x\n", status.longitude );
printf("status.latitude = %x\n", status.latitude );
}
设置 status.longitude = 0x02;
有效地将 ūnion
中的所有其他变量设置为相同的值。
输出为:
Union Size = 16
status.longitude = 2
status.latitude = 2