将位字段转换为整型

16

我已经按照这种方式声明了一个位域:

typedef struct morder {
    unsigned int targetRegister : 3;
    unsigned int targetMethodOfAddressing : 3;
    unsigned int originRegister : 3;
    unsigned int originMethodOfAddressing : 3;
    unsigned int oCode : 4;
} bitset;

我也有一个int数组,我想从这个数组中获取int值,该值表示此位域的实际值(实际上是我拥有其部分的某种机器字,我希望获得整个字的int表示)。


3
@shaharg:我认为你的措辞不够精确。位域是结构体中的各个字段,但你似乎将整个结构体称为“位域”。 - JXG
4个回答

28

请不要使用联合体。或者说,在使用联合体之前,请了解你正在做什么。

正如你可以在这个答案中看到的那样,请不要依赖于位域的可移植性。特别是对于你的情况,结构体内部的位域排列顺序是与实现相关的。

现在,如果你的问题是,如何将位域结构体打印为int进行私人审查,那么使用联合体是很好的选择。但你似乎想要位域的“实际值”。

所以:如果你只在这一台机器/编译器组合上工作,并且你不需要依赖于int的数学值,只要它有意义,那么你可以使用联合体。但如果你可能会迁移代码,或者如果你需要位域的“实际值”,你需要编写位操作代码来将位域放入正确的int位中。


1
实现相关是指编译器相关、平台相关,还是两者都有关? - gsgx
1
@gsingh2011,都可以。特定架构的特定编译器可以做它认为最好的事情。 - JXG
2
@mahesh,并不是必须的。排序取决于具体实现,因此编译器可以随意处理。排序可能没有数学意义。联合将为您提供一致的位字段到整数的映射,但您无法事先知道这些整数是否是数学值。如果您移植代码,每个映射可能都会有所不同。 - JXG
2
@Mahesh 值得注意的是,读取最近未被写入的联合成员是未定义行为。我认为大多数编译器会按照你的意愿执行,但这是需要记在心里的事情。 - youngmit
@youngmit,自从应用了C99技术勘误3以来,这不是未定义行为。这种操作(从不同的联合成员读取)的结果是_实现定义_,如果目标成员由于此类操作而包含陷阱表示,则可能导致未定义行为,但本身并不是未定义行为。 - Alex Che

16

你可以使用联合体:

typedef union bitsetConvertor {
    bitset bs;
    uint16_t i;
} bitsetConvertor;

bitsetConvertor convertor;
convertor.i = myInt;
bitset bs = convertor.bs;

或者您可以使用强制类型转换:

bitset bs = *(bitset *)&myInt;

或者您可以在联合体内使用匿名结构:

typedef union morder {
    struct {
        unsigned int targetRegister : 3;
        unsigned int targetMethodOfAddressing : 3;
        unsigned int originRegister : 3;
        unsigned int originMethodOfAddressing : 3;
        unsigned int oCode : 4;
    };

    uint16_t intRepresentation;
} bitset;

bitset bs;
bs.intRepresentation = myInt;

11
被投反对了:bitset bs = *(bitset *)&myInt; 是违反严格别名规则的经典案例。 - user694733
5
uint16_t无法保证等同于unsigned int。联合体的实现依赖于具体情况,位域结构几乎没有关于它们布局的任何保证。强制类型转换违反了有效类型(又称严格别名)规则 -> 未定义的行为。这是一种不好的方法。 - too honest for this site

3

只需使用联合体。然后,您可以将数据作为16位整数或作为单独的位字段访问,例如:

#include <stdio.h>
#include <stdint.h>

typedef struct {
    unsigned int targetRegister : 3;
    unsigned int targetMethodOfAddressing : 3;
    unsigned int originRegister : 3;
    unsigned int originMethodOfAddressing : 3;
    unsigned int oCode : 4;
} bitset;

typedef union {
    bitset b;
    uint16_t i;
} u_bitset;

int main(void)
{
    u_bitset u = {{0}};
    
    u.b.originRegister = 1;
    printf("u.i = %#x\n", u.i); 

    return 0;
}

0

你可以简单地做

typedef struct morder {
  unsigned int targetRegister : 3;
  unsigned int targetMethodOfAddressing : 3;
  unsigned int originRegister : 3;
  unsigned int originMethodOfAddressing : 3;
  unsigned int oCode : 4;
} bitset;

short result;
std::memcpy(&result, &bitset, sizeof(short));

通过这种方式,将bitset的内存区域复制到另一个内存区域,并将其解释为一个shortresult是一个short,因为您的位集的大小为16位(2字节)。
如果您想要更好地设置新缓冲区的内存大小,可以使用以下方法(使用c++11或更高版本)。
std::memcpy(&result, &bitset, sizeof(decltype(result));

另一种解决方案(非常丑陋和危险)是重新解释内存区域:
short result = *reinterpret_cast<short*>(&bitset);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接